weapp-ide-cli 6.2.0 → 6.4.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.
@@ -1,9 +1,9 @@
1
- import { D as logger, E as colors, T as operatingSystemName, _ as readCustomConfig, a as assertWechatDevtoolsPort, b as defaultCustomConfigFilePath, d as resolveDevtoolsAutomationDefaults, f as createAutoBootstrapDevtoolsConfig, g as overwriteCustomConfig, h as createLocaleConfig, i as assertWechatDevtoolsHost, l as getConfig, m as createCustomConfig, p as createAutoTrustProjectConfig, r as resolveWechatDevtoolsTarget, u as getConfiguredLocale, v as removeCustomConfigKey, w as isOperatingSystemSupported, x as resolvePath } from "./sessionStore-zdzy2UFM.js";
2
- import { a as readManagedRecord, c as withManagedJournalLock, f as assertManagedInstallation, l as writeManagedRecord, m as sameManagedProcess, o as readManagedWechatProjectRecords, p as inspectManagedProjectHost, s as resolveManagedJournal } from "./devtoolsProjectOwnership-yDD6h0dO.js";
3
- import { _ as parseAutomatorPort, a as bootstrapWechatDevtoolsSettings, g as resolveAutomatorSessionOptions, o as detectWechatDevtoolsServicePort } from "./automator-CRpB6_pA.js";
4
- import { C as runWithSuspendedSharedInput, D as validateLocaleOption, E as i18nText, T as configureLocaleFromArgv, _ as isWechatIdeLoginRequiredError, b as promptWechatIdeLoginRetry, c as withMiniProgram$1, i as connectConsoleMiniProgram, l as runRetryableCommand, p as createWechatIdeLoginRequiredExitError, r as closeSharedMiniProgram, s as releaseSharedMiniProgram, t as acquireConsoleMiniProgram } from "./automator-session-gWEsSs_A.js";
5
- import { a as navigateBack, c as pageStack, d as remote, f as scrollTo, g as tap, i as input, l as reLaunch, m as systemInfo, n as captureScreenshotBuffer, o as navigateTo, p as switchTab, r as currentPage, s as pageData, t as audit, u as redirectTo } from "./commands-C1PF1B4P.js";
6
- import "./run-mcp-Dl_1TH1x.js";
1
+ import { A as operatingSystemName, B as removeCustomConfigKey, C as registerWechatDevtoolsInstallation, F as createAutoTrustProjectConfig, H as defaultCustomConfigFilePath, I as createCustomConfig, L as createLocaleConfig, M as logger, N as wechatDevtoolsSelectionContext, P as createAutoBootstrapDevtoolsConfig, R as overwriteCustomConfig, S as selectInstalledWechatDevtools, U as resolvePath, d as validateWechatDevtoolsSelection, f as withWechatDevtoolsSelection, j as colors, k as isOperatingSystemSupported, m as assertWechatDevtoolsPort, p as assertWechatDevtoolsHost, u as resolveWechatDevtoolsTarget, w as unregisterWechatDevtoolsInstallation, x as listWechatDevtoolsInstallations, z as readCustomConfig } from "./automatorProject-DPU-pEF0.js";
2
+ import { a as isManagedWechatProjectFullyReleased, c as resolveManagedJournal, d as assertManagedInstallation, f as inspectManagedProjectHost, l as withManagedJournalLock, o as readManagedRecord, p as sameManagedProcess, s as readManagedWechatProjectRecords, u as writeManagedRecord } from "./devtoolsProjectOwnership-C4jj8msU.js";
3
+ import { C as isWechatIdeLoginRequiredError, E as promptWechatIdeLoginRetry, F as parseAutomatorPort, M as i18nText, N as validateLocaleOption, P as resolveAutomatorSessionOptions, a as bootstrapWechatDevtoolsSettings, j as configureLocaleFromArgv, k as runWithSuspendedSharedInput, o as detectWechatDevtoolsServicePort, y as createWechatIdeLoginRequiredExitError } from "./automator-Esp3BjoT.js";
4
+ import { a as connectConsoleMiniProgram, c as releaseSharedMiniProgram, i as closeSharedMiniProgram, l as withMiniProgram$1, r as closeOwnedSharedMiniProgram, t as acquireConsoleMiniProgram, u as runRetryableCommand } from "./automator-session-Ba27VW0_.js";
5
+ import { a as navigateBack, c as pageStack, d as remote, f as scrollTo, g as tap, i as input, l as reLaunch, m as systemInfo, n as captureScreenshotBuffer, o as navigateTo, p as switchTab, r as currentPage, s as pageData, t as audit, u as redirectTo } from "./commands-5entj0Rh.js";
6
+ import "./run-mcp-CbmskSgr.js";
7
7
  import path from "node:path";
8
8
  import { inspect } from "node:util";
9
9
  import { withMachineE2ELease } from "@weapp-vite/devtools-runtime";
@@ -16,7 +16,7 @@ import { PNG } from "pngjs";
16
16
  import pixelmatch from "pixelmatch";
17
17
  import { createInterface } from "node:readline/promises";
18
18
  import { cac } from "cac";
19
- //#region src/cli/automator-argv.ts
19
+ // src/cli/automator-argv.ts
20
20
  function parsePositiveInt(raw) {
21
21
  const value = Number.parseInt(raw, 10);
22
22
  if (!Number.isFinite(value) || value <= 0) return;
@@ -185,8 +185,7 @@ function removeOption(argv, optionName) {
185
185
  }
186
186
  return nextArgv;
187
187
  }
188
- //#endregion
189
- //#region src/cli/imageDiff.ts
188
+ // src/cli/imageDiff.ts
190
189
  function readPng(buffer, label) {
191
190
  try {
192
191
  return PNG.sync.read(buffer);
@@ -224,8 +223,7 @@ async function comparePngWithBaseline(options) {
224
223
  diffRatio: diffPixels / (currentPng.width * currentPng.height)
225
224
  };
226
225
  }
227
- //#endregion
228
- //#region src/cli/compare.ts
226
+ // src/cli/compare.ts
229
227
  function createCliError(message, exitCode, cause) {
230
228
  const error = new Error(message, cause ? { cause } : void 0);
231
229
  error.exitCode = exitCode;
@@ -257,7 +255,7 @@ ${colors.bold("参数:")}
257
255
  --diff-output <path> diff 图片输出路径
258
256
  --page <path> 对比前先跳转页面
259
257
  --full-page 对比时使用整页长截图
260
- --threshold <number> pixelmatch threshold(默认:0.1)
258
+ --threshold <number> OKLab/HyAB 感知色差阈值(默认:0.1)
261
259
  --max-diff-pixels <count> 最大允许差异像素数
262
260
  --max-diff-ratio <number> 最大允许差异占比(0-1)
263
261
  -t, --timeout <ms> 连接超时时间(默认:30000)
@@ -286,7 +284,7 @@ ${colors.bold("Options:")}
286
284
  --diff-output <path> Output file path for diff image
287
285
  --page <path> Navigate to page before comparison
288
286
  --full-page Use stitched full-page screenshots for comparison
289
- --threshold <number> Pixelmatch threshold (default: 0.1)
287
+ --threshold <number> OKLab/HyAB perceptual threshold (default: 0.1)
290
288
  --max-diff-pixels <count> Maximum allowed diff pixels
291
289
  --max-diff-ratio <number> Maximum allowed diff ratio (0-1)
292
290
  -t, --timeout <ms> Connection timeout in milliseconds (default: 30000)
@@ -392,8 +390,7 @@ async function runCompare(argv) {
392
390
  else printCompareSummary(result);
393
391
  if (!result.passed) process.exitCode = 1;
394
392
  }
395
- //#endregion
396
- //#region src/cli/screenshot.ts
393
+ // src/cli/screenshot.ts
397
394
  /**
398
395
  * @description Print help for screenshot command
399
396
  */
@@ -494,7 +491,7 @@ async function runScreenshot(argv) {
494
491
  }
495
492
  const options = parseScreenshotArgs(argv);
496
493
  const isJsonOutput = argv.includes("--json");
497
- const { takeScreenshot } = await import("./commands-CZVPfkE9.js");
494
+ const { takeScreenshot } = await import("./commands-DR-8MVFm.js");
498
495
  const result = await takeScreenshot(options);
499
496
  if (isJsonOutput) {
500
497
  console.log(JSON.stringify(result, null, 2));
@@ -502,8 +499,7 @@ async function runScreenshot(argv) {
502
499
  }
503
500
  if (result.base64) console.log(result.base64);
504
501
  }
505
- //#endregion
506
- //#region src/cli/runtime-service.ts
502
+ // src/cli/runtime-service.ts
507
503
  const DEFAULT_RUNTIME_SERVICE_URL = "http://127.0.0.1:3088/api/weapp/devtools";
508
504
  const SERVICE_UNAVAILABLE_CODES = /* @__PURE__ */ new Set([
509
505
  "ECONNREFUSED",
@@ -631,8 +627,7 @@ async function tryRunRuntimeServiceCommand(command, argv) {
631
627
  const args = parseAutomatorArgs(argv);
632
628
  return await runRouteCommand(command, args, baseUrl) || await runInfoCommand(command, args, baseUrl);
633
629
  }
634
- //#endregion
635
- //#region src/cli/run-automator.ts
630
+ // src/cli/run-automator.ts
636
631
  const COMMON_OPTION_DEFINITIONS = [
637
632
  {
638
633
  flag: "--cli-path <path>",
@@ -1022,8 +1017,7 @@ async function runAutomatorCommand(command, argv) {
1022
1017
  args
1023
1018
  }));
1024
1019
  }
1025
- //#endregion
1026
- //#region src/cli/command-catalog.ts
1020
+ // src/cli/command-catalog.ts
1027
1021
  const WECHAT_CLI_COMMAND_NAMES = [
1028
1022
  "open",
1029
1023
  "login",
@@ -1055,6 +1049,7 @@ const WEAPP_IDE_TOP_LEVEL_COMMAND_NAMES = [
1055
1049
  ...WECHAT_CLI_COMMAND_NAMES,
1056
1050
  ...AUTOMATOR_COMMAND_NAMES,
1057
1051
  ...MINIDEV_NAMESPACE_COMMAND_NAMES,
1052
+ "devtools",
1058
1053
  CONFIG_COMMAND_NAME,
1059
1054
  "mcp"
1060
1055
  ];
@@ -1065,8 +1060,91 @@ const WEAPP_IDE_TOP_LEVEL_COMMAND_SET = new Set(WEAPP_IDE_TOP_LEVEL_COMMAND_NAME
1065
1060
  function isWeappIdeTopLevelCommand(command) {
1066
1061
  return Boolean(command && WEAPP_IDE_TOP_LEVEL_COMMAND_SET.has(command));
1067
1062
  }
1068
- //#endregion
1069
- //#region src/cli/prompt.ts
1063
+ // src/config/resolver.ts
1064
+ function isCustomConfigJson(value) {
1065
+ return typeof value === "object" && value !== null;
1066
+ }
1067
+ /**
1068
+ * @description 解析开发者工具自动预热相关配置的最终生效值。
1069
+ */
1070
+ function resolveDevtoolsAutomationDefaults(config) {
1071
+ return {
1072
+ autoBootstrapDevtools: config.autoBootstrapDevtools ?? true,
1073
+ autoTrustProject: config.autoTrustProject ?? false
1074
+ };
1075
+ }
1076
+ /**
1077
+ * @description 读取并解析 CLI 配置(进程选择优先,其次用户配置与平台默认)
1078
+ */
1079
+ async function getConfig() {
1080
+ const frozen = wechatDevtoolsSelectionContext.getStore();
1081
+ if (frozen) return {
1082
+ ...await readCustomConfig(),
1083
+ cliPath: frozen.cliPath,
1084
+ source: "environment"
1085
+ };
1086
+ if (process.env.WEAPP_IDE_DEVTOOLS_CHANNEL?.trim() || process.env.WEAPP_IDE_DEVTOOLS_VERSION?.trim()) {
1087
+ const target = await resolveWechatDevtoolsTarget();
1088
+ return {
1089
+ ...await readCustomConfig(),
1090
+ cliPath: target.cliPath,
1091
+ source: "environment"
1092
+ };
1093
+ }
1094
+ const selectedCliPath = process.env.WEAPP_IDE_CLI_PATH?.trim();
1095
+ if (await fs$1.pathExists(defaultCustomConfigFilePath)) try {
1096
+ const rawConfig = await fs$1.readJSON(defaultCustomConfigFilePath);
1097
+ const config = isCustomConfigJson(rawConfig) ? rawConfig : {};
1098
+ const cliPath = typeof config.cliPath === "string" ? config.cliPath.trim() : "";
1099
+ const locale = config.locale === "zh" || config.locale === "en" ? config.locale : void 0;
1100
+ const autoBootstrapDevtools = typeof config.autoBootstrapDevtools === "boolean" ? config.autoBootstrapDevtools : void 0;
1101
+ const autoTrustProject = typeof config.autoTrustProject === "boolean" ? config.autoTrustProject : void 0;
1102
+ if (selectedCliPath || cliPath) {
1103
+ logger.info(`全局配置文件路径:${colors.green(defaultCustomConfigFilePath)}`);
1104
+ logger.info(`${selectedCliPath ? "进程指定" : "自定义"} CLI 路径:${colors.green(selectedCliPath || cliPath)}`);
1105
+ return {
1106
+ cliPath: selectedCliPath || cliPath,
1107
+ locale,
1108
+ autoBootstrapDevtools,
1109
+ autoTrustProject,
1110
+ source: selectedCliPath ? "environment" : "custom"
1111
+ };
1112
+ }
1113
+ logger.warn("自定义配置文件缺少有效的 CLI 路径,将尝试使用默认路径。");
1114
+ } catch (error) {
1115
+ const reason = error instanceof Error ? error.message : String(error);
1116
+ logger.warn(`解析自定义配置失败,${selectedCliPath ? "继续使用进程指定路径" : "将尝试使用默认路径"}。原因:${reason}`);
1117
+ }
1118
+ if (selectedCliPath) return {
1119
+ cliPath: selectedCliPath,
1120
+ source: "environment"
1121
+ };
1122
+ const fallbackPath = await selectInstalledWechatDevtools().then((target) => target.cliPath, (error) => {
1123
+ if (error.code === "WECHAT_DEVTOOLS_INSTALLATION_NOT_FOUND") return;
1124
+ throw error;
1125
+ });
1126
+ if (fallbackPath) return {
1127
+ cliPath: fallbackPath,
1128
+ locale: void 0,
1129
+ autoBootstrapDevtools: void 0,
1130
+ autoTrustProject: void 0,
1131
+ source: "default"
1132
+ };
1133
+ return {
1134
+ cliPath: "",
1135
+ locale: void 0,
1136
+ autoBootstrapDevtools: void 0,
1137
+ autoTrustProject: void 0,
1138
+ source: "missing"
1139
+ };
1140
+ }
1141
+ /**
1142
+ * @description 获取用户配置的语言偏好。
1143
+ */
1144
+ async function getConfiguredLocale() {
1145
+ return (await readCustomConfig()).locale;
1146
+ }
1147
+ // src/cli/prompt.ts
1070
1148
  /**
1071
1149
  * @description 交互式提示并保存 CLI 路径
1072
1150
  */
@@ -1102,8 +1180,7 @@ async function promptForCliPath() {
1102
1180
  }
1103
1181
  });
1104
1182
  }
1105
- //#endregion
1106
- //#region src/cli/resolver.ts
1183
+ // src/cli/resolver.ts
1107
1184
  /**
1108
1185
  * @description 解析 CLI 路径并校验可用性
1109
1186
  */
@@ -1118,8 +1195,7 @@ async function resolveCliPath() {
1118
1195
  source: config.source
1119
1196
  };
1120
1197
  }
1121
- //#endregion
1122
- //#region src/cli/config-command.ts
1198
+ // src/cli/config-command.ts
1123
1199
  function parseBooleanConfigValue(rawValue) {
1124
1200
  const normalized = rawValue.trim().toLowerCase();
1125
1201
  if (normalized === "true" || normalized === "1" || normalized === "on") return true;
@@ -1273,8 +1349,126 @@ async function handleConfigCommand(argv) {
1273
1349
  }
1274
1350
  throw new Error(i18nText("支持的 config 子命令:lang | set-lang | show | get | set | unset | doctor | import | export", "Supported config subcommands: lang | set-lang | show | get | set | unset | doctor | import | export"));
1275
1351
  }
1276
- //#endregion
1277
- //#region src/cli/managedProjectGate.ts
1352
+ // src/cli/devtoolsSelection.ts
1353
+ const selectionOptions$1 = /* @__PURE__ */ new Map([
1354
+ ["--cli-path", "cliPath"],
1355
+ ["--devtools-channel", "devtoolsChannel"],
1356
+ ["--devtools-version", "devtoolsVersion"]
1357
+ ]);
1358
+ const valueOptions = /* @__PURE__ */ new Set([
1359
+ "--project",
1360
+ "-p",
1361
+ "--desc",
1362
+ "-d",
1363
+ "--version",
1364
+ "-v",
1365
+ "--uv",
1366
+ "--appid",
1367
+ "--ext-appid",
1368
+ "--info-output",
1369
+ "-i",
1370
+ "--result-output",
1371
+ "-r",
1372
+ "--qr-output",
1373
+ "--qr-format",
1374
+ "--qr-size",
1375
+ "--port",
1376
+ "--timeout",
1377
+ "-t",
1378
+ "--session-id",
1379
+ "--output",
1380
+ "-o",
1381
+ "--page",
1382
+ "--lang",
1383
+ "--platform",
1384
+ "--runtime-url",
1385
+ "--login-retry",
1386
+ "--login-retry-timeout",
1387
+ "--config",
1388
+ "-c",
1389
+ "--mode",
1390
+ "-m",
1391
+ "--filter",
1392
+ "-f",
1393
+ "--base",
1394
+ "--logLevel",
1395
+ "-l",
1396
+ "--project-config",
1397
+ "--logPath",
1398
+ "--ticket",
1399
+ "--account",
1400
+ "--test-ticket",
1401
+ "--compile-condition",
1402
+ "--compile-type",
1403
+ "--clean",
1404
+ "--auto-port",
1405
+ "--auto-account",
1406
+ "--replay-config-path",
1407
+ "--key-store",
1408
+ "--key-alias",
1409
+ "--key-pass",
1410
+ "--store-pass",
1411
+ "--use-aab",
1412
+ "--resourceBundleVersion",
1413
+ "--resourceBundleDesc",
1414
+ "--isDistribute",
1415
+ "--isRemoteBuild",
1416
+ "--profilePath",
1417
+ "--certificateName",
1418
+ "--p12Path",
1419
+ "--p12Password",
1420
+ "--tpnsProfilePath",
1421
+ "--isUploadBeta",
1422
+ "--versionName",
1423
+ "--versionCode",
1424
+ "--versionDesc"
1425
+ ]);
1426
+ /** 包装参数不会透传官方 CLI,选项值和双横线后的内容保持原样。 */
1427
+ function splitWechatDevtoolsSelectionArgs(argv) {
1428
+ const selection = {};
1429
+ const args = [];
1430
+ for (let index = 0; index < argv.length; index++) {
1431
+ const token = argv[index];
1432
+ if (token === "--") {
1433
+ args.push(...argv.slice(index));
1434
+ break;
1435
+ }
1436
+ const equals = token.indexOf("=");
1437
+ const name = equals < 0 ? token : token.slice(0, equals);
1438
+ const key = selectionOptions$1.get(name);
1439
+ if (key) {
1440
+ const value = equals < 0 ? argv[++index] : token.slice(equals + 1);
1441
+ if (!value?.trim() || value.startsWith("-")) throw new Error(`${name} requires a value.`);
1442
+ if (selection[key] !== void 0 && selection[key] !== value.trim()) throw new Error(`Conflicting ${name} values in the same command.`);
1443
+ Object.assign(selection, { [key]: value.trim() });
1444
+ } else if (name === "--require-official-latest") {
1445
+ const value = equals < 0 ? "true" : token.slice(equals + 1);
1446
+ if (![
1447
+ "true",
1448
+ "false",
1449
+ "1",
1450
+ "0"
1451
+ ].includes(value)) throw new Error("--require-official-latest requires a boolean.");
1452
+ const enabled = value === "true" || value === "1";
1453
+ if (selection.requireOfficialLatest !== void 0 && selection.requireOfficialLatest !== enabled) throw new Error("Conflicting --require-official-latest values in the same command.");
1454
+ selection.requireOfficialLatest = enabled;
1455
+ } else {
1456
+ args.push(token);
1457
+ if (equals < 0 && valueOptions.has(name) && argv[index + 1] !== void 0) args.push(argv[++index]);
1458
+ }
1459
+ }
1460
+ validateWechatDevtoolsSelection(selection);
1461
+ return {
1462
+ args,
1463
+ selection
1464
+ };
1465
+ }
1466
+ /** 显式选择在任何 helper 或 CLI 回退之前固定,避免入口间选版漂移。 */
1467
+ async function withWechatDevtoolsSelectionArgs(argv, runner) {
1468
+ const { args, selection } = splitWechatDevtoolsSelectionArgs(argv);
1469
+ return Object.keys(selection).length ? await withWechatDevtoolsSelection(selection, () => runner(args)) : await runner(args);
1470
+ }
1471
+ // src/cli/managedProjectGate.ts
1278
1472
  function sameTarget(first, second) {
1279
1473
  return first.installationId === second.installationId && first.cliPath === second.cliPath && first.appPath === second.appPath && first.profileDir === second.profileDir && first.version === second.version && first.channel === second.channel;
1280
1474
  }
@@ -1286,7 +1480,7 @@ async function findConfirmedProject(journalPath, target, projectPath) {
1286
1480
  "unconfirmed",
1287
1481
  "closing",
1288
1482
  "failed"
1289
- ].includes(record.state))) throw new Error("Managed DevTools journal contains unresolved ownership or cleanup; refusing a project mutation.");
1483
+ ].includes(record.state) || record.state === "released" && !isManagedWechatProjectFullyReleased(record) || record.state === "borrowed" && record.snapshot?.state !== void 0 && record.snapshot.state !== "removed")) throw new Error("Managed DevTools journal contains unresolved ownership or cleanup; refusing a project mutation.");
1290
1484
  return scopeRoot === journalPath ? scopeRecords : await readManagedWechatProjectRecords(journalPath);
1291
1485
  })).filter((record) => record.state !== "released" && record.projectPath === projectPath && sameTarget(record.target, target));
1292
1486
  for (const record of matching) try {
@@ -1319,8 +1513,8 @@ async function ensureManagedWechatProject(target, projectPath, options = {}) {
1319
1513
  await assertManagedInstallation(target);
1320
1514
  options.signal?.throwIfAborted();
1321
1515
  if (existing) return existing;
1322
- const { launchAutomator } = await import("./automator-BkZZsGtn.js");
1323
- (await launchAutomator({
1516
+ const { launchAutomator } = await import("./automator-BqhUtMJp.js");
1517
+ await (await launchAutomator({
1324
1518
  target,
1325
1519
  cliPath: target.cliPath,
1326
1520
  projectPath: resolvedProjectPath,
@@ -1337,8 +1531,7 @@ async function ensureManagedWechatProject(target, projectPath, options = {}) {
1337
1531
  return confirmed;
1338
1532
  });
1339
1533
  }
1340
- //#endregion
1341
- //#region src/cli/wechatDevtoolsRuntimePort.ts
1534
+ // src/cli/wechatDevtoolsRuntimePort.ts
1342
1535
  const runtimePorts = /* @__PURE__ */ new Map();
1343
1536
  /**
1344
1537
  * @description 标记所选安装检测到的服务端口;无身份的旧调用仅保留在独立兼容槽中。
@@ -1357,8 +1550,7 @@ function setRuntimeWechatDevtoolsServicePort(port, target) {
1357
1550
  function getRuntimeWechatDevtoolsServicePort(target) {
1358
1551
  return runtimePorts.get(target?.installationId ?? "");
1359
1552
  }
1360
- //#endregion
1361
- //#region src/cli/http.ts
1553
+ // src/cli/http.ts
1362
1554
  const ENGINE_BUILD_NOT_START = "NOT_START";
1363
1555
  const ENGINE_BUILD_OPEN_PROJECT = "OPEN_PROJECT";
1364
1556
  const ENGINE_BUILD_BUILDING = "BUILDING";
@@ -1482,8 +1674,7 @@ const WECHAT_DEVTOOLS_ENGINE_BUILD_STATUSES = {
1482
1674
  NOT_START: ENGINE_BUILD_NOT_START,
1483
1675
  OPEN_PROJECT: ENGINE_BUILD_OPEN_PROJECT
1484
1676
  };
1485
- //#endregion
1486
- //#region src/cli/engine.ts
1677
+ // src/cli/engine.ts
1487
1678
  function createEngineBuildError(message, code) {
1488
1679
  const error = new Error(message);
1489
1680
  error.code = code;
@@ -1698,8 +1889,30 @@ async function prepareAcceptanceProject(projectPath, signal, options = {}) {
1698
1889
  });
1699
1890
  });
1700
1891
  }
1701
- //#endregion
1702
- //#region src/cli/forwardConsole.ts
1892
+ // src/cli/forwardConsole/refresh.ts
1893
+ /** 刷新已有项目的日志开关,并保留日志启用与连接释放的独立错误。 */
1894
+ async function refreshOpenedMiniProgramConsoleLog(options, enableLog) {
1895
+ const miniProgram = await connectConsoleMiniProgram({
1896
+ ...options,
1897
+ openedOnly: true
1898
+ });
1899
+ let failure;
1900
+ let failed = false;
1901
+ try {
1902
+ await enableLog(miniProgram);
1903
+ } catch (error) {
1904
+ failed = true;
1905
+ failure = error;
1906
+ }
1907
+ try {
1908
+ await miniProgram.disconnect();
1909
+ } catch (cleanupError) {
1910
+ if (failed) throw new AggregateError([failure, cleanupError], "Console log refresh and connection cleanup failed", { cause: failure });
1911
+ throw cleanupError;
1912
+ }
1913
+ if (failed) throw failure;
1914
+ }
1915
+ // src/cli/forwardConsole.ts
1703
1916
  const DEFAULT_FORWARD_CONSOLE_LEVELS = [
1704
1917
  "log",
1705
1918
  "info",
@@ -1842,29 +2055,28 @@ async function acquireForwardConsoleMiniProgram(options) {
1842
2055
  return miniProgram;
1843
2056
  } catch (error) {
1844
2057
  lastError = error;
1845
- if (miniProgram) releaseSharedMiniProgram(options.projectPath, options.sessionId || options.port, options);
1846
- await closeSharedMiniProgram(options.projectPath, options.sessionId || options.port, options);
2058
+ const cleanupErrors = [];
2059
+ if (miniProgram) try {
2060
+ await releaseSharedMiniProgram(options.projectPath, options.sessionId || options.port, options);
2061
+ } catch (cleanupError) {
2062
+ cleanupErrors.push(cleanupError);
2063
+ }
2064
+ try {
2065
+ await closeSharedMiniProgram(options.projectPath, options.sessionId || options.port, options);
2066
+ } catch (cleanupError) {
2067
+ cleanupErrors.push(cleanupError);
2068
+ }
2069
+ if (cleanupErrors.length) throw new AggregateError([error, ...cleanupErrors], "Forward console setup and connection cleanup failed", { cause: error });
1847
2070
  if (attempt < ENABLE_LOG_RETRY_TIMES) await sleep(ENABLE_LOG_RETRY_DELAY_MS);
1848
2071
  }
1849
2072
  }
1850
2073
  throw lastError instanceof Error ? lastError : new Error(String(lastError));
1851
2074
  }
1852
- async function refreshOpenedMiniProgramConsoleLog(options) {
1853
- const miniProgram = await connectConsoleMiniProgram({
1854
- ...options,
1855
- openedOnly: true
1856
- });
1857
- try {
1858
- await enableMiniProgramConsoleLog(miniProgram);
1859
- } finally {
1860
- miniProgram.disconnect();
1861
- }
1862
- }
1863
2075
  function startEnableLogRefresh(miniProgram, options, refreshOpenedSession) {
1864
2076
  if (typeof miniProgram.enableLog !== "function") return;
1865
2077
  const timer = setInterval(() => {
1866
2078
  miniProgram.enableLog(ENABLE_LOG_TIMEOUT_MS).catch(() => {});
1867
- if (refreshOpenedSession) refreshOpenedMiniProgramConsoleLog(options).catch(() => {});
2079
+ if (refreshOpenedSession) refreshOpenedMiniProgramConsoleLog(options, enableMiniProgramConsoleLog).catch(() => {});
1868
2080
  }, ENABLE_LOG_REFRESH_INTERVAL_MS);
1869
2081
  return () => {
1870
2082
  clearInterval(timer);
@@ -1900,8 +2112,10 @@ async function startForwardConsole(options) {
1900
2112
  let stopEnableLogRefresh;
1901
2113
  let auxiliaryMiniProgram;
1902
2114
  let auxiliaryTimer;
1903
- const closeSession = async () => {
1904
- if (closed) return;
2115
+ let closing;
2116
+ let sharedReleased = false;
2117
+ const closeSession = () => {
2118
+ if (closing) return closing;
1905
2119
  closed = true;
1906
2120
  if (auxiliaryTimer) {
1907
2121
  clearTimeout(auxiliaryTimer);
@@ -1911,13 +2125,33 @@ async function startForwardConsole(options) {
1911
2125
  stopEnableLogRefresh = void 0;
1912
2126
  detachMiniProgramListener(miniProgram, "console", onConsole);
1913
2127
  detachMiniProgramListener(miniProgram, "exception", onException);
1914
- if (auxiliaryMiniProgram) {
1915
- detachMiniProgramListener(auxiliaryMiniProgram, "console", onConsole);
1916
- detachMiniProgramListener(auxiliaryMiniProgram, "exception", onException);
1917
- auxiliaryMiniProgram.disconnect();
1918
- auxiliaryMiniProgram = void 0;
1919
- }
1920
- if (usesSharedSession) releaseSharedMiniProgram(options.projectPath, options.sessionId || options.port, options);
2128
+ closing = (async () => {
2129
+ const errors = [];
2130
+ if (auxiliaryMiniProgram) {
2131
+ detachMiniProgramListener(auxiliaryMiniProgram, "console", onConsole);
2132
+ detachMiniProgramListener(auxiliaryMiniProgram, "exception", onException);
2133
+ try {
2134
+ await auxiliaryMiniProgram.disconnect();
2135
+ auxiliaryMiniProgram = void 0;
2136
+ } catch (error) {
2137
+ errors.push(error);
2138
+ }
2139
+ }
2140
+ if (usesSharedSession) try {
2141
+ if (!sharedReleased) {
2142
+ sharedReleased = true;
2143
+ await releaseSharedMiniProgram(options.projectPath, options.sessionId || options.port, options);
2144
+ }
2145
+ await closeOwnedSharedMiniProgram(options, miniProgram);
2146
+ } catch (error) {
2147
+ errors.push(error);
2148
+ }
2149
+ if (errors.length) throw new AggregateError(errors, "Forward console session cleanup failed");
2150
+ })().catch((error) => {
2151
+ closing = void 0;
2152
+ throw error;
2153
+ });
2154
+ return closing;
1921
2155
  };
1922
2156
  miniProgram.on("console", onConsole);
1923
2157
  miniProgram.on("exception", onException);
@@ -1929,7 +2163,7 @@ async function startForwardConsole(options) {
1929
2163
  openedOnly: true
1930
2164
  });
1931
2165
  if (closed) {
1932
- openedMiniProgram.disconnect();
2166
+ await openedMiniProgram.disconnect();
1933
2167
  return;
1934
2168
  }
1935
2169
  auxiliaryMiniProgram = openedMiniProgram;
@@ -1941,8 +2175,7 @@ async function startForwardConsole(options) {
1941
2175
  options.onReady?.();
1942
2176
  return { close: closeSession };
1943
2177
  }
1944
- //#endregion
1945
- //#region src/utils/argv.ts
2178
+ // src/utils/argv.ts
1946
2179
  function ensurePathArgument(argv, optionIndex) {
1947
2180
  const paramIdx = optionIndex + 1;
1948
2181
  const param = argv[paramIdx];
@@ -1980,8 +2213,7 @@ function createPathCompat(option) {
1980
2213
  return ensurePathArgument(argv, optionIndex);
1981
2214
  };
1982
2215
  }
1983
- //#endregion
1984
- //#region src/utils/exec.ts
2216
+ // src/utils/exec.ts
1985
2217
  /**
1986
2218
  * @description 执行 CLI 命令并透传输出
1987
2219
  */
@@ -1997,8 +2229,7 @@ async function execute(cliPath, argv, options = {}) {
1997
2229
  if (pipeStderr) task?.stderr?.pipe(process.stderr);
1998
2230
  return await task;
1999
2231
  }
2000
- //#endregion
2001
- //#region src/cli/loginQuery.ts
2232
+ // src/cli/loginQuery.ts
2002
2233
  /** 读取原生 CLI 的明确登录结果;日志、空输出与相互矛盾的响应均不能证明登录状态。 */
2003
2234
  function parseLoginOutput(stdout) {
2004
2235
  const login = readWechatLoginState(stdout);
@@ -2052,8 +2283,7 @@ async function queryWechatIdeLogin(cliPath, options = {}) {
2052
2283
  };
2053
2284
  }
2054
2285
  }
2055
- //#endregion
2056
- //#region src/cli/minidev.ts
2286
+ // src/cli/minidev.ts
2057
2287
  const MINIDEV_COMMAND = "minidev";
2058
2288
  function isCommandNotFound(error) {
2059
2289
  return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
@@ -2076,8 +2306,22 @@ async function runMinidev(argv) {
2076
2306
  throw error;
2077
2307
  }
2078
2308
  }
2079
- //#endregion
2080
- //#region src/cli/run-login-config.ts
2309
+ // src/cli/devtools.ts
2310
+ /** 本地安装管理不启动 IDE,不更改账号或默认 CLI 配置。 */
2311
+ async function handleDevtoolsCommand(argv) {
2312
+ const [action = "list", cliPath] = argv;
2313
+ if (action === "list") {
2314
+ const installations = await listWechatDevtoolsInstallations();
2315
+ if (argv.includes("--json")) console.log(JSON.stringify({ installations }, null, 2));
2316
+ else for (const entry of installations) console.log(entry.target ? `${entry.target.channel ?? "unknown"}@${entry.target.version ?? "unknown"}\n CLI: ${entry.cliPath}\n installation: ${entry.target.installationId}\n profile: ${entry.target.profileDir}` : `unavailable: ${entry.cliPath}\n ${entry.error}`);
2317
+ return;
2318
+ }
2319
+ if (action !== "add" && action !== "remove") throw new Error("Use weapp devtools list [--json] | add <path-to-cli> | remove <path-to-cli>.");
2320
+ if (!cliPath || argv.length !== 2) throw new Error(`weapp devtools ${action} requires one CLI path.`);
2321
+ if (action === "add") console.log(JSON.stringify(await registerWechatDevtoolsInstallation(cliPath), null, 2));
2322
+ else await unregisterWechatDevtoolsInstallation(cliPath);
2323
+ }
2324
+ // src/cli/run-login-config.ts
2081
2325
  function normalizeLoginRetryMode(value, nonInteractive) {
2082
2326
  if (value && value !== "never" && value !== "once" && value !== "always") throw new Error(i18nText(`不支持的 --login-retry 值: ${value}(仅支持 never/once/always)`, `Invalid --login-retry value: ${value} (supported: never/once/always)`));
2083
2327
  if (value === "never" || value === "once" || value === "always") return value;
@@ -2114,8 +2358,7 @@ function resolveLoginRetryConfig(argv) {
2114
2358
  runtimeArgv: stripLoginRetryControlFlags(argv)
2115
2359
  };
2116
2360
  }
2117
- //#endregion
2118
- //#region src/cli/run-login.ts
2361
+ // src/cli/run-login.ts
2119
2362
  const IDE_SERVER_STARTED_RE = /IDE server has started,\s*listening on\s+https?:\/\/127\.0\.0\.1:(\d+)/i;
2120
2363
  function unwrapWechatCliExecutionError(result) {
2121
2364
  return result.kind === "retryable" ? result.error : result.value;
@@ -2217,8 +2460,7 @@ async function executeWechatCliWithRetry(cliPath, argv, options) {
2217
2460
  async function runWechatCliWithRetry(cliPath, argv, options = {}) {
2218
2461
  return await withMachineE2ELease(() => executeWechatCliWithRetry(cliPath, argv, options));
2219
2462
  }
2220
- //#endregion
2221
- //#region src/cli/run-wechat-cli.ts
2463
+ // src/cli/run-wechat-cli.ts
2222
2464
  function shouldBootstrapWechatDevtools(command) {
2223
2465
  return command === "open" || command === "auto" || command === "auto-preview";
2224
2466
  }
@@ -2274,6 +2516,7 @@ async function resolveWechatCliCommandTarget(options = {}) {
2274
2516
  logger.warn(i18nText(`微信web开发者工具不支持当前平台:${operatingSystemName} !`, `Wechat Web DevTools CLI is not supported on current platform: ${operatingSystemName}!`));
2275
2517
  return;
2276
2518
  }
2519
+ if (options.cliPath || options.devtoolsChannel || options.devtoolsVersion || options.requireOfficialLatest) return await resolveWechatDevtoolsTarget(options);
2277
2520
  let target = options.target;
2278
2521
  if (!target) {
2279
2522
  const { cliPath, source } = await resolveCliPath();
@@ -2307,8 +2550,7 @@ async function executeWechatCliCommand(argv, options) {
2307
2550
  async function runWechatCliCommand(argv, options = {}) {
2308
2551
  return await withMachineE2ELease(() => executeWechatCliCommand(argv, options));
2309
2552
  }
2310
- //#endregion
2311
- //#region src/cli/wechat-command-schema.ts
2553
+ // src/cli/wechat-command-schema.ts
2312
2554
  const CACHE_CLEAN_TYPES = [
2313
2555
  "storage",
2314
2556
  "file",
@@ -2391,8 +2633,21 @@ function validateWechatCliCommandArgs(argv) {
2391
2633
  if (!isNonEmptyText(output) || isDistribute === void 0) throw new Error(i18nText("build-ipa 命令缺少必填参数:--output/-o 和 --isDistribute", "build-ipa command requires both --output/-o and --isDistribute"));
2392
2634
  }
2393
2635
  }
2394
- //#endregion
2395
- //#region src/cli/wechat-commands.ts
2636
+ // src/cli/wechat-commands.ts
2637
+ function selectionOptions(options) {
2638
+ return Object.fromEntries([
2639
+ "cliPath",
2640
+ "target",
2641
+ "devtoolsChannel",
2642
+ "devtoolsVersion",
2643
+ "requireOfficialLatest"
2644
+ ].filter((key) => options[key] !== void 0).map((key) => [key, options[key]]));
2645
+ }
2646
+ async function runSelectedCommand(argv, options) {
2647
+ const selection = selectionOptions(options);
2648
+ if (Object.keys(selection).length) await runWechatCliCommand(argv, selection);
2649
+ else await runWechatCliCommand(argv);
2650
+ }
2396
2651
  const DEVTOOLS_SHUTDOWN_TIMEOUT_MS = 1e4;
2397
2652
  function appendProjectLocatorArgv(argv, options) {
2398
2653
  if (options.projectPath) argv.push("--project", path.resolve(options.projectPath));
@@ -2418,7 +2673,7 @@ async function openSelectedWechatIde(options, target) {
2418
2673
  /** 打开与兼容回退在同一机器租约内沿用固定安装,禁止重新选择宿主。 */
2419
2674
  async function openWechatIde(options = {}) {
2420
2675
  return await withMachineE2ELease(async () => {
2421
- const target = await resolveWechatCliCommandTarget();
2676
+ const target = await resolveWechatCliCommandTarget(options);
2422
2677
  if (target) {
2423
2678
  if (process.env["WEAPP_IDE_MANAGED_PROJECT_JOURNAL"]?.trim()) {
2424
2679
  if (!options.projectPath) throw new Error("Managed DevTools open requires an explicit project path; no project was opened.");
@@ -2438,7 +2693,7 @@ async function loginWechatIde(options = {}) {
2438
2693
  if (options.qrOutput) argv.push("--qr-output", path.resolve(options.qrOutput));
2439
2694
  if (options.qrSize) argv.push("--qr-size", options.qrSize);
2440
2695
  if (options.resultOutput) argv.push("--result-output", path.resolve(options.resultOutput));
2441
- await runWechatCliCommand(argv);
2696
+ await runSelectedCommand(argv, options);
2442
2697
  }
2443
2698
  /**
2444
2699
  * @description 调用微信开发者工具 islogin 命令。
@@ -2446,8 +2701,11 @@ async function loginWechatIde(options = {}) {
2446
2701
  async function isWechatIdeLoggedIn(options = {}) {
2447
2702
  const argv = ["islogin"];
2448
2703
  if (options.nonInteractive) argv.push("--non-interactive");
2449
- if (options.silent) await runWechatCliCommand(argv, { silent: true });
2450
- else await runWechatCliCommand(argv);
2704
+ if (options.silent) await runWechatCliCommand(argv, {
2705
+ ...selectionOptions(options),
2706
+ silent: true
2707
+ });
2708
+ else await runSelectedCommand(argv, options);
2451
2709
  }
2452
2710
  /**
2453
2711
  * @description 调用微信开发者工具 build-npm 命令。
@@ -2459,7 +2717,7 @@ async function buildWechatIdeNpm(options = {}) {
2459
2717
  path.resolve(options.projectPath ?? process.cwd())
2460
2718
  ];
2461
2719
  if (options.compileType) argv.push("--compile-type", options.compileType);
2462
- await runWechatCliCommand(argv);
2720
+ await runSelectedCommand(argv, options);
2463
2721
  }
2464
2722
  /**
2465
2723
  * @description 调用微信开发者工具 preview 命令。
@@ -2472,7 +2730,7 @@ async function previewWechatIde(options = {}) {
2472
2730
  if (options.qrSize) argv.push("--qr-size", options.qrSize);
2473
2731
  if (options.infoOutput) argv.push("--info-output", path.resolve(options.infoOutput));
2474
2732
  if (options.compileCondition) argv.push("--compile-condition", options.compileCondition);
2475
- await runWechatCliCommand(argv);
2733
+ await runSelectedCommand(argv, options);
2476
2734
  }
2477
2735
  /**
2478
2736
  * @description 调用微信开发者工具 auto-preview 命令。
@@ -2482,7 +2740,7 @@ async function autoPreviewWechatIde(options = {}) {
2482
2740
  appendProjectLocatorArgv(argv, options);
2483
2741
  if (options.infoOutput) argv.push("--info-output", path.resolve(options.infoOutput));
2484
2742
  if (options.compileCondition) argv.push("--compile-condition", options.compileCondition);
2485
- await runWechatCliCommand(argv);
2743
+ await runSelectedCommand(argv, options);
2486
2744
  }
2487
2745
  /**
2488
2746
  * @description 调用微信开发者工具 auto 命令。
@@ -2495,7 +2753,7 @@ async function autoWechatIde(options = {}) {
2495
2753
  if (options.testTicket) argv.push("--test-ticket", options.testTicket);
2496
2754
  if (options.ticket) argv.push("--ticket", options.ticket);
2497
2755
  if (options.trustProject) argv.push("--trust-project");
2498
- await runWechatCliCommand(argv);
2756
+ await runSelectedCommand(argv, options);
2499
2757
  }
2500
2758
  /**
2501
2759
  * @description 调用微信开发者工具 auto-replay 命令。
@@ -2510,7 +2768,7 @@ async function autoReplayWechatIde(options = {}) {
2510
2768
  if (options.testTicket) argv.push("--test-ticket", options.testTicket);
2511
2769
  if (options.ticket) argv.push("--ticket", options.ticket);
2512
2770
  if (options.trustProject) argv.push("--trust-project");
2513
- await runWechatCliCommand(argv);
2771
+ await runSelectedCommand(argv, options);
2514
2772
  }
2515
2773
  /**
2516
2774
  * @description 调用微信开发者工具 upload 命令。
@@ -2520,7 +2778,7 @@ async function uploadWechatIde(options) {
2520
2778
  appendProjectLocatorArgv(argv, options);
2521
2779
  argv.push("--version", options.version, "--desc", options.desc);
2522
2780
  if (options.infoOutput) argv.push("--info-output", path.resolve(options.infoOutput));
2523
- await runWechatCliCommand(argv);
2781
+ await runSelectedCommand(argv, options);
2524
2782
  }
2525
2783
  /**
2526
2784
  * @description 调用微信开发者工具 close 命令。
@@ -2530,45 +2788,50 @@ async function closeWechatIdeProject(options = {}) {
2530
2788
  if (options.cliPath !== void 0 && !options.cliPath.trim()) throw new Error("The explicit close cliPath must not be empty.");
2531
2789
  const argv = ["close"];
2532
2790
  appendProjectLocatorArgv(argv, options);
2533
- const target = options.target || options.cliPath ? await resolveWechatDevtoolsTarget({
2534
- target: options.target,
2535
- cliPath: options.cliPath
2536
- }) : void 0;
2791
+ const target = options.target || options.cliPath || options.devtoolsChannel || options.devtoolsVersion ? await resolveWechatDevtoolsTarget(selectionOptions(options)) : void 0;
2537
2792
  await runWechatCliCommand(argv, {
2538
- ...target ? { target } : {},
2793
+ ...target ? { target } : selectionOptions(options),
2539
2794
  timeout: DEVTOOLS_SHUTDOWN_TIMEOUT_MS
2540
2795
  });
2541
2796
  }
2542
2797
  /**
2543
2798
  * @description 调用微信开发者工具 quit 命令。
2544
2799
  */
2545
- async function quitWechatIde() {
2546
- await runWechatCliCommand(["quit"], { timeout: DEVTOOLS_SHUTDOWN_TIMEOUT_MS });
2800
+ async function quitWechatIde(options = {}) {
2801
+ await runWechatCliCommand(["quit"], {
2802
+ ...selectionOptions(options),
2803
+ timeout: DEVTOOLS_SHUTDOWN_TIMEOUT_MS
2804
+ });
2547
2805
  }
2548
2806
  /**
2549
2807
  * @description 调用微信开发者工具 cache 命令。
2550
2808
  */
2551
2809
  async function clearWechatIdeCache(options) {
2552
- await runWechatCliCommand([
2810
+ await runSelectedCommand([
2553
2811
  "cache",
2554
2812
  "--clean",
2555
2813
  options.clean
2556
- ]);
2814
+ ], options);
2557
2815
  }
2558
2816
  /**
2559
2817
  * @description 调用微信开发者工具 open-other 命令。
2560
2818
  */
2561
- async function openWechatIdeOtherProject(_options = {}) {
2562
- await runWechatCliCommand(["open-other"]);
2819
+ async function openWechatIdeOtherProject(options = {}) {
2820
+ await runSelectedCommand(["open-other"], options);
2563
2821
  }
2564
2822
  /**
2565
2823
  * @description 通过微信开发者工具 HTTP 服务端口重置指定项目的 fileutils 状态。
2566
2824
  */
2567
2825
  async function resetWechatIdeFileUtils(options) {
2568
- await resetWechatIdeFileUtilsByHttp(path.resolve(options.projectPath));
2826
+ const selection = selectionOptions(options);
2827
+ if (Object.keys(selection).length) await resetWechatIdeFileUtilsByHttp(path.resolve(options.projectPath), selection);
2828
+ else await resetWechatIdeFileUtilsByHttp(path.resolve(options.projectPath));
2569
2829
  }
2570
2830
  function createAutomatorSessionOptions(options) {
2571
2831
  return {
2832
+ cliPath: options.cliPath,
2833
+ target: options.target,
2834
+ ...selectionOptions(options),
2572
2835
  ...options.port ? { port: options.port } : {},
2573
2836
  preferOpenedSession: options.preferOpenedSession ?? true,
2574
2837
  preserveProjectRoot: options.preserveProjectRoot,
@@ -2656,7 +2919,7 @@ async function buildWechatIdeApk(options) {
2656
2919
  if (options.isUploadResourceBundle) argv.push("--isUploadResourceBundle");
2657
2920
  if (options.resourceBundleVersion) argv.push("--resourceBundleVersion", options.resourceBundleVersion);
2658
2921
  if (options.resourceBundleDesc) argv.push("--resourceBundleDesc", options.resourceBundleDesc);
2659
- await runWechatCliCommand(argv);
2922
+ await runSelectedCommand(argv, options);
2660
2923
  }
2661
2924
  /**
2662
2925
  * @description 调用微信开发者工具 build-ipa 命令。
@@ -2682,10 +2945,9 @@ async function buildWechatIdeIpa(options) {
2682
2945
  if (options.versionName) argv.push("--versionName", options.versionName);
2683
2946
  if (options.versionCode !== void 0) argv.push("--versionCode", String(options.versionCode));
2684
2947
  if (options.versionDesc) argv.push("--versionDesc", options.versionDesc);
2685
- await runWechatCliCommand(argv);
2948
+ await runSelectedCommand(argv, options);
2686
2949
  }
2687
- //#endregion
2688
- //#region src/cli/wechat-dispatch.ts
2950
+ // src/cli/wechat-dispatch.ts
2689
2951
  function hasOption(argv, optionName) {
2690
2952
  return argv.includes(optionName) || argv.some((token) => token.startsWith(`${optionName}=`));
2691
2953
  }
@@ -2874,8 +3136,7 @@ async function dispatchWechatCliCommand(argv) {
2874
3136
  }
2875
3137
  return false;
2876
3138
  }
2877
- //#endregion
2878
- //#region src/cli/run.ts
3139
+ // src/cli/run.ts
2879
3140
  const MINIDEV_NAMESPACE = new Set(MINIDEV_NAMESPACE_COMMAND_NAMES);
2880
3141
  const ALIPAY_PLATFORM_ALIASES = new Set(MINIDEV_NAMESPACE_COMMAND_NAMES);
2881
3142
  const ARG_TRANSFORMS = [
@@ -2898,6 +3159,11 @@ function createCli() {
2898
3159
  cli.command("help [command]", "显示命令帮助").allowUnknownOptions();
2899
3160
  for (const command of WECHAT_CLI_COMMAND_NAMES) cli.command(command, "微信开发者工具官方命令透传").allowUnknownOptions();
2900
3161
  for (const command of MINIDEV_NAMESPACE_COMMAND_NAMES) cli.command(`${command} [...args]`, "支付宝 minidev 命令透传").allowUnknownOptions();
3162
+ cli.command("devtools [...args]", "管理本机多版本开发者工具").allowUnknownOptions();
3163
+ cli.option("--devtools-channel <channel>", "选择 stable | rc | nightly");
3164
+ cli.option("--devtools-version <version>", "固定开发者工具产品版本");
3165
+ cli.option("--cli-path <path>", "固定开发者工具 CLI 安装路径");
3166
+ cli.option("--require-official-latest", "要求匹配所选渠道的官方最新版本");
2901
3167
  cli.command(`${CONFIG_COMMAND_NAME} [...args]`, "配置 weapp-ide-cli").allowUnknownOptions();
2902
3168
  cli.command(`mcp [...args]`, "启动 weapp-ide-cli MCP 服务").allowUnknownOptions();
2903
3169
  for (const command of AUTOMATOR_COMMAND_NAMES) cli.command(`${command} [...args]`, "automator 增强命令").allowUnknownOptions();
@@ -2946,7 +3212,7 @@ function createMinidevOpenArgv(argv) {
2946
3212
  /**
2947
3213
  * @description CLI 入口解析与分发。
2948
3214
  */
2949
- async function parse(argv) {
3215
+ async function parseSelected(argv) {
2950
3216
  validateLocaleOption(argv);
2951
3217
  configureLocaleFromArgv(argv, await getConfiguredLocale());
2952
3218
  const cli = createCli();
@@ -2968,12 +3234,16 @@ async function parse(argv) {
2968
3234
  await runMinidev(argv.slice(1));
2969
3235
  return;
2970
3236
  }
3237
+ if (matchedCommand === "devtools") {
3238
+ await handleDevtoolsCommand(argv.slice(1));
3239
+ return;
3240
+ }
2971
3241
  if (matchedCommand === "config") {
2972
3242
  await handleConfigCommand(argv.slice(1));
2973
3243
  return;
2974
3244
  }
2975
3245
  if (matchedCommand === "mcp") {
2976
- const { runMcpCommand } = await import("./run-mcp-CPv8cesh.js");
3246
+ const { runMcpCommand } = await import("./run-mcp-D1w7-oPp.js");
2977
3247
  await runMcpCommand(argv.slice(1));
2978
3248
  return;
2979
3249
  }
@@ -2990,5 +3260,16 @@ async function parse(argv) {
2990
3260
  if (await dispatchWechatCliCommand(formattedArgv)) return;
2991
3261
  await runWechatCliCommand(formattedArgv);
2992
3262
  }
2993
- //#endregion
2994
- export { MINIDEV_NAMESPACE_COMMAND_NAMES as $, queryWechatIdeLogin as A, WECHAT_DEVTOOLS_ENGINE_BUILD_STATUSES as B, refreshWechatIdeTicket as C, validateWechatCliCommandArgs as D, uploadWechatIde as E, startForwardConsole as F, startWechatIdeEngineBuildByHttp as G, pollWechatIdeEngineBuildResultByHttp as H, isWechatIdeEngineBuildEndpointMissingError as I, handleConfigCommand as J, getRuntimeWechatDevtoolsServicePort as K, prepareAcceptanceProject as L, createAlias as M, createPathCompat as N, runWechatCliWithRetry as O, transformArgv as P, MCP_COMMAND_NAME as Q, runWechatIdeEngineBuild as R, quitWechatIde as S, setWechatIdeTicket as T, requestWechatDevtoolsHttp as U, openWechatIdeProjectByHttp as V, resetWechatIdeFileUtilsByHttp as W, promptForCliPath as X, resolveCliPath as Y, CONFIG_COMMAND_NAME as Z, isWechatIdeLoggedIn as _, autoReplayWechatIde as a, isAutomatorCommand as at, openWechatIdeOtherProject as b, buildWechatIdeIpa as c, printScreenshotHelp as ct, clearWechatIdeCacheByAutomator as d, parseAutomatorArgs as dt, WEAPP_IDE_TOP_LEVEL_COMMAND_NAMES as et, closeWechatIdeProject as f, readBooleanOption as ft, getWechatIdeToolInfo as g, getWechatIdeTicket as h, autoPreviewWechatIde as i, getAutomatorCommandHelp as it, execute as j, runMinidev as k, buildWechatIdeNpm as l, parseCompareArgs as lt, getWechatIdeTestAccounts as m, removeOption as mt, parse as n, isWeappIdeTopLevelCommand as nt, autoWechatIde as o, runAutomatorCommand as ot, compileWechatIdeByAutomator as p, readOptionValue as pt, setRuntimeWechatDevtoolsServicePort as q, dispatchWechatCliCommand as r, AUTOMATOR_COMMAND_NAMES as rt, buildWechatIdeApk as s, parseScreenshotArgs as st, createCli as t, WECHAT_CLI_COMMAND_NAMES as tt, clearWechatIdeCache as u, printCompareHelp as ut, loginWechatIde as v, resetWechatIdeFileUtils as w, previewWechatIde as x, openWechatIde as y, runWechatIdeEngineBuildByHttp as z };
3263
+ /** 在分发前剥离公共选择参数并固定本次安装。 */
3264
+ async function parse(argv) {
3265
+ if ([
3266
+ "help",
3267
+ "config",
3268
+ "devtools",
3269
+ "alipay",
3270
+ "ali",
3271
+ "minidev"
3272
+ ].includes(argv[0] ?? "") || argv.includes("--help") || argv.includes("-h")) return await parseSelected(argv);
3273
+ return await withWechatDevtoolsSelectionArgs(argv, parseSelected);
3274
+ }
3275
+ export { getConfig as $, queryWechatIdeLogin as A, WECHAT_DEVTOOLS_ENGINE_BUILD_STATUSES as B, refreshWechatIdeTicket as C, validateWechatCliCommandArgs as D, uploadWechatIde as E, startForwardConsole as F, startWechatIdeEngineBuildByHttp as G, pollWechatIdeEngineBuildResultByHttp as H, isWechatIdeEngineBuildEndpointMissingError as I, splitWechatDevtoolsSelectionArgs as J, getRuntimeWechatDevtoolsServicePort as K, prepareAcceptanceProject as L, createAlias as M, createPathCompat as N, runWechatCliWithRetry as O, transformArgv as P, promptForCliPath as Q, runWechatIdeEngineBuild as R, quitWechatIde as S, setWechatIdeTicket as T, requestWechatDevtoolsHttp as U, openWechatIdeProjectByHttp as V, resetWechatIdeFileUtilsByHttp as W, handleConfigCommand as X, withWechatDevtoolsSelectionArgs as Y, resolveCliPath as Z, isWechatIdeLoggedIn as _, readBooleanOption as _t, autoReplayWechatIde as a, WEAPP_IDE_TOP_LEVEL_COMMAND_NAMES as at, openWechatIdeOtherProject as b, buildWechatIdeIpa as c, AUTOMATOR_COMMAND_NAMES as ct, clearWechatIdeCacheByAutomator as d, runAutomatorCommand as dt, getConfiguredLocale as et, closeWechatIdeProject as f, parseScreenshotArgs as ft, getWechatIdeToolInfo as g, parseAutomatorArgs as gt, getWechatIdeTicket as h, printCompareHelp as ht, autoPreviewWechatIde as i, MINIDEV_NAMESPACE_COMMAND_NAMES as it, execute as j, runMinidev as k, buildWechatIdeNpm as l, getAutomatorCommandHelp as lt, getWechatIdeTestAccounts as m, parseCompareArgs as mt, parse as n, CONFIG_COMMAND_NAME as nt, autoWechatIde as o, WECHAT_CLI_COMMAND_NAMES as ot, compileWechatIdeByAutomator as p, printScreenshotHelp as pt, setRuntimeWechatDevtoolsServicePort as q, dispatchWechatCliCommand as r, MCP_COMMAND_NAME as rt, buildWechatIdeApk as s, isWeappIdeTopLevelCommand as st, createCli as t, resolveDevtoolsAutomationDefaults as tt, clearWechatIdeCache as u, isAutomatorCommand as ut, loginWechatIde as v, readOptionValue as vt, resetWechatIdeFileUtils as w, previewWechatIde as x, openWechatIde as y, removeOption as yt, runWechatIdeEngineBuildByHttp as z };