weapp-ide-cli 6.1.9 → 6.3.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,15 +1,191 @@
1
- import { $ as createAutoTrustProjectConfig, D as validateLocaleOption, E as i18nText, G as resolveDevtoolsAutomationDefaults, H as resolveCliPath, Q as createAutoBootstrapDevtoolsConfig, T as configureLocaleFromArgv, W as getConfiguredLocale, X as colors, Z as logger_default, et as createCustomConfig, i as connectConsoleMiniProgram, it as removeCustomConfigKey, nt as overwriteCustomConfig, ot as defaultCustomConfigFilePath, r as closeSharedMiniProgram, rt as readCustomConfig, s as releaseSharedMiniProgram, t as acquireConsoleMiniProgram, tt as createLocaleConfig } from "./automator-session-fbanL2eW.js";
2
- import { A as isWechatIdeLoggedIn, B as runWechatCliCommand, C as clearWechatIdeCache, F as quitWechatIde, G as transformArgv, H as execute, K as promptForCliPath, L as resetWechatIdeFileUtils, M as openWechatIde, N as openWechatIdeOtherProject, P as previewWechatIde, Q as startWechatIdeEngineBuildByHttp, S as buildWechatIdeNpm, T as closeWechatIdeProject, U as createAlias, W as createPathCompat, Y as pollWechatIdeEngineBuildResultByHttp, _ as autoPreviewWechatIde, a as navigateBack, b as buildWechatIdeApk, c as pageStack, d as remote, f as scrollTo, g as tap, i as input, it as removeOption, j as loginWechatIde, l as reLaunch, m as systemInfo, n as captureScreenshotBuffer, nt as readBooleanOption, o as navigateTo, p as switchTab, r as currentPage, rt as readOptionValue, s as pageData, t as audit, tt as parseAutomatorArgs, u as redirectTo, v as autoReplayWechatIde, x as buildWechatIdeIpa, y as autoWechatIde, z as uploadWechatIde } from "./commands-__RJ552L.js";
3
- import "./run-mcp-LqaWlQJ_.js";
4
- import fs from "node:fs/promises";
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-DjYLqMj6.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-BMfSAg4n.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-BOe32upt.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-VOB03glx.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-mslf7-sP.js";
6
+ import "./run-mcp-Ni139Z5C.js";
5
7
  import path from "node:path";
8
+ import { inspect } from "node:util";
9
+ import { withMachineE2ELease } from "@weapp-vite/devtools-runtime";
10
+ import { execa } from "execa";
11
+ import fs from "node:fs/promises";
12
+ import process, { stdin, stdout } from "node:process";
6
13
  import { fs as fs$1 } from "@weapp-core/shared/fs";
7
- import process from "node:process";
14
+ import { OperationLifecycle, readWechatLoginState } from "@weapp-vite/miniprogram-automator/operation";
8
15
  import { PNG } from "pngjs";
9
16
  import pixelmatch from "pixelmatch";
10
- import { execa } from "execa";
11
- import { inspect } from "node:util";
17
+ import { createInterface } from "node:readline/promises";
12
18
  import { cac } from "cac";
19
+ //#region src/cli/automator-argv.ts
20
+ function parsePositiveInt(raw) {
21
+ const value = Number.parseInt(raw, 10);
22
+ if (!Number.isFinite(value) || value <= 0) return;
23
+ return value;
24
+ }
25
+ function takesValue(optionName) {
26
+ return optionName === "-p" || optionName === "--project" || optionName === "-t" || optionName === "--timeout" || optionName === "--cli-path" || optionName === "--port" || optionName === "--session-id" || optionName === "-o" || optionName === "--output" || optionName === "--page" || optionName === "--login-retry" || optionName === "--login-retry-timeout" || optionName === "--lang" || optionName === "--platform" || optionName === "--runtime-url" || optionName === "--qr-output" || optionName === "-r" || optionName === "--result-output" || optionName === "--info-output" || optionName === "-i";
27
+ }
28
+ /**
29
+ * @description 解析 automator 命令通用参数与位置参数。
30
+ */
31
+ function parseAutomatorArgs(argv) {
32
+ const positionals = [];
33
+ let projectPath = process.cwd();
34
+ let cliPath;
35
+ let timeout;
36
+ let port;
37
+ let sessionId;
38
+ let preferOpenedSession;
39
+ let json = false;
40
+ for (let index = 0; index < argv.length; index += 1) {
41
+ const token = argv[index];
42
+ if (!token) continue;
43
+ if (token === "-p" || token === "--project") {
44
+ const value = argv[index + 1];
45
+ if (typeof value === "string" && !value.startsWith("-")) {
46
+ projectPath = value;
47
+ index += 1;
48
+ } else projectPath = process.cwd();
49
+ continue;
50
+ }
51
+ if (token.startsWith("--project=")) {
52
+ projectPath = token.slice(10) || process.cwd();
53
+ continue;
54
+ }
55
+ if (token === "--cli-path") {
56
+ const value = argv[index + 1];
57
+ if (value && !value.startsWith("-")) {
58
+ cliPath = value;
59
+ index += 1;
60
+ }
61
+ continue;
62
+ }
63
+ if (token.startsWith("--cli-path=")) {
64
+ cliPath = token.slice(11) || void 0;
65
+ continue;
66
+ }
67
+ if (token === "-t" || token === "--timeout") {
68
+ const value = argv[index + 1];
69
+ if (typeof value === "string") {
70
+ timeout = parsePositiveInt(value);
71
+ index += 1;
72
+ }
73
+ continue;
74
+ }
75
+ if (token.startsWith("--timeout=")) {
76
+ timeout = parsePositiveInt(token.slice(10));
77
+ continue;
78
+ }
79
+ if (token === "--port") {
80
+ port = parseAutomatorPort(argv[index + 1]);
81
+ index += 1;
82
+ continue;
83
+ }
84
+ if (token.startsWith("--port=")) {
85
+ port = parseAutomatorPort(token.slice(7));
86
+ continue;
87
+ }
88
+ if (token === "--session-id") {
89
+ const value = argv[index + 1];
90
+ if (typeof value === "string" && !value.startsWith("-")) {
91
+ sessionId = value.trim() || void 0;
92
+ index += 1;
93
+ }
94
+ continue;
95
+ }
96
+ if (token.startsWith("--session-id=")) {
97
+ sessionId = token.slice(13).trim() || void 0;
98
+ continue;
99
+ }
100
+ if (token === "--json") {
101
+ json = true;
102
+ continue;
103
+ }
104
+ if (token === "--no-runtime-service") {
105
+ preferOpenedSession = false;
106
+ continue;
107
+ }
108
+ if (token.startsWith("-")) {
109
+ if (takesValue(token.includes("=") ? token.slice(0, token.indexOf("=")) : token) && !token.includes("=")) {
110
+ const value = argv[index + 1];
111
+ if (typeof value === "string" && !value.startsWith("-")) index += 1;
112
+ }
113
+ continue;
114
+ }
115
+ positionals.push(token);
116
+ }
117
+ return {
118
+ ...cliPath ? { cliPath } : {},
119
+ projectPath,
120
+ ...timeout ? { timeout } : {},
121
+ ...port ? { port } : {},
122
+ ...sessionId ? { sessionId } : {},
123
+ ...preferOpenedSession === false ? { preferOpenedSession } : {},
124
+ json,
125
+ positionals
126
+ };
127
+ }
128
+ /**
129
+ * @description 读取选项值,支持多个别名,以及 --option value 与 --option=value。
130
+ */
131
+ function readOptionValue(argv, ...optionNames) {
132
+ for (const optionName of optionNames) {
133
+ const optionWithEqual = `${optionName}=`;
134
+ for (let index = 0; index < argv.length; index += 1) {
135
+ const token = argv[index];
136
+ if (!token) continue;
137
+ if (token === optionName) {
138
+ const value = argv[index + 1];
139
+ if (typeof value !== "string") return;
140
+ return value.trim();
141
+ }
142
+ if (token.startsWith(optionWithEqual)) return token.slice(optionWithEqual.length).trim();
143
+ }
144
+ }
145
+ }
146
+ /**
147
+ * @description 读取布尔选项,支持裸 flag、--flag=true/false 与 --flag true/false。
148
+ */
149
+ function readBooleanOption(argv, ...optionNames) {
150
+ for (const optionName of optionNames) {
151
+ const optionWithEqual = `${optionName}=`;
152
+ for (let index = 0; index < argv.length; index += 1) {
153
+ const token = argv[index];
154
+ if (!token) continue;
155
+ if (token === optionName) {
156
+ const nextToken = argv[index + 1];
157
+ if (nextToken === "true") return true;
158
+ if (nextToken === "false") return false;
159
+ return true;
160
+ }
161
+ if (token.startsWith(optionWithEqual)) {
162
+ const rawValue = token.slice(optionWithEqual.length).trim().toLowerCase();
163
+ if (rawValue === "true") return true;
164
+ if (rawValue === "false") return false;
165
+ }
166
+ }
167
+ }
168
+ }
169
+ /**
170
+ * @description 删除参数中的指定选项(同时支持 --opt value 与 --opt=value)。
171
+ */
172
+ function removeOption(argv, optionName) {
173
+ const optionWithEqual = `${optionName}=`;
174
+ const nextArgv = [];
175
+ for (let index = 0; index < argv.length; index += 1) {
176
+ const token = argv[index];
177
+ if (!token) continue;
178
+ if (token === optionName) {
179
+ const nextToken = argv[index + 1];
180
+ if (takesValue(optionName) && typeof nextToken === "string" && !nextToken.startsWith("-")) index += 1;
181
+ continue;
182
+ }
183
+ if (token.startsWith(optionWithEqual)) continue;
184
+ nextArgv.push(token);
185
+ }
186
+ return nextArgv;
187
+ }
188
+ //#endregion
13
189
  //#region src/cli/imageDiff.ts
14
190
  function readPng(buffer, label) {
15
191
  try {
@@ -298,6 +474,7 @@ function parseScreenshotArgs(argv) {
298
474
  const fullPage = argv.includes("--full-page");
299
475
  return {
300
476
  projectPath: parsed.projectPath,
477
+ ...parsed.cliPath ? { cliPath: parsed.cliPath } : {},
301
478
  ...parsed.timeout ? { timeout: parsed.timeout } : {},
302
479
  ...parsed.port ? { port: parsed.port } : {},
303
480
  ...parsed.sessionId ? { sessionId: parsed.sessionId } : {},
@@ -317,7 +494,7 @@ async function runScreenshot(argv) {
317
494
  }
318
495
  const options = parseScreenshotArgs(argv);
319
496
  const isJsonOutput = argv.includes("--json");
320
- const { takeScreenshot } = await import("./commands-DpMG1w04.js");
497
+ const { takeScreenshot } = await import("./commands-DmirGBy4.js");
321
498
  const result = await takeScreenshot(options);
322
499
  if (isJsonOutput) {
323
500
  console.log(JSON.stringify(result, null, 2));
@@ -350,8 +527,13 @@ function resolveOutputPath(outputPath) {
350
527
  return outputPath ? path.resolve(outputPath) : void 0;
351
528
  }
352
529
  function createConnectionPayload(args) {
530
+ const cliPath = args.cliPath ?? process.env.WEAPP_IDE_CLI_PATH;
353
531
  return {
354
532
  projectPath: resolveProjectPath(args.projectPath),
533
+ ...cliPath ? { cliPath } : {},
534
+ ...args.port ? { port: args.port } : {},
535
+ ...args.sessionId ? { sessionId: args.sessionId } : {},
536
+ ...args.preferOpenedSession !== void 0 ? { preferOpenedSession: args.preferOpenedSession } : {},
355
537
  ...args.timeout ? { timeout: args.timeout } : {}
356
538
  };
357
539
  }
@@ -405,7 +587,7 @@ async function runRouteCommand(command, args, baseUrl) {
405
587
  }, (args.timeout ?? 3e4) + 5e3);
406
588
  if (result === void 0) return false;
407
589
  if (args.json) printJsonResult(result);
408
- else logger_default.success(`已通过 runtime service 执行 ${command}:${formatRuntimeServiceUrl(baseUrl)}`);
590
+ else logger.success(`已通过 runtime service 执行 ${command}:${formatRuntimeServiceUrl(baseUrl)}`);
409
591
  return true;
410
592
  }
411
593
  async function runInfoCommand(command, args, baseUrl) {
@@ -424,20 +606,18 @@ async function runScreenshotCommand(argv, baseUrl) {
424
606
  if (options.fullPage) return false;
425
607
  if (options.page) {
426
608
  if (await tryRequestRuntimeService(baseUrl, "/route", {
427
- projectPath: resolveProjectPath(options.projectPath),
428
- ...options.timeout ? { timeout: options.timeout } : {},
609
+ ...createConnectionPayload(options),
429
610
  path: options.page,
430
611
  transition: "reLaunch"
431
612
  }, (options.timeout ?? 3e4) + 5e3) === void 0) return false;
432
613
  }
433
614
  const result = await tryRequestRuntimeService(baseUrl, "/capture", {
434
- projectPath: resolveProjectPath(options.projectPath),
435
- ...options.timeout ? { timeout: options.timeout } : {},
615
+ ...createConnectionPayload(options),
436
616
  ...options.outputPath ? { outputPath: resolveOutputPath(options.outputPath) } : {}
437
617
  }, (options.timeout ?? 3e4) + 5e3);
438
618
  if (result === void 0) return false;
439
619
  if (argv.includes("--json")) printJsonResult(options.outputPath ? { path: options.outputPath } : result);
440
- else if (options.outputPath) logger_default.success(`截图已通过 runtime service 保存到 ${colors.cyan(options.outputPath)}`);
620
+ else if (options.outputPath) logger.success(`截图已通过 runtime service 保存到 ${colors.cyan(options.outputPath)}`);
441
621
  else if (result.base64) process.stdout.write(`${result.base64}\n`);
442
622
  return true;
443
623
  }
@@ -454,6 +634,13 @@ async function tryRunRuntimeServiceCommand(command, argv) {
454
634
  //#endregion
455
635
  //#region src/cli/run-automator.ts
456
636
  const COMMON_OPTION_DEFINITIONS = [
637
+ {
638
+ flag: "--cli-path <path>",
639
+ description: {
640
+ zh: "指定微信开发者工具安装",
641
+ en: "Select the WeChat DevTools installation"
642
+ }
643
+ },
457
644
  {
458
645
  flag: "-p, --project <path>",
459
646
  description: {
@@ -519,6 +706,7 @@ const COMMON_OPTION_DEFINITIONS = [
519
706
  }
520
707
  ];
521
708
  const COMMON_ALLOWED_OPTIONS = /* @__PURE__ */ new Set([
709
+ "--cli-path",
522
710
  "-p",
523
711
  "--project",
524
712
  "-t",
@@ -805,7 +993,7 @@ function printCommandHelp(command) {
805
993
  console.log(help);
806
994
  return;
807
995
  }
808
- logger_default.warn(i18nText(`命令 ${command} 暂无帮助信息`, `No help available for command: ${command}`));
996
+ logger.warn(i18nText(`命令 ${command} 暂无帮助信息`, `No help available for command: ${command}`));
809
997
  }
810
998
  /**
811
999
  * @description 执行 automator 子命令。
@@ -867,6 +1055,7 @@ const WEAPP_IDE_TOP_LEVEL_COMMAND_NAMES = [
867
1055
  ...WECHAT_CLI_COMMAND_NAMES,
868
1056
  ...AUTOMATOR_COMMAND_NAMES,
869
1057
  ...MINIDEV_NAMESPACE_COMMAND_NAMES,
1058
+ "devtools",
870
1059
  CONFIG_COMMAND_NAME,
871
1060
  "mcp"
872
1061
  ];
@@ -878,6 +1067,144 @@ function isWeappIdeTopLevelCommand(command) {
878
1067
  return Boolean(command && WEAPP_IDE_TOP_LEVEL_COMMAND_SET.has(command));
879
1068
  }
880
1069
  //#endregion
1070
+ //#region src/config/resolver.ts
1071
+ function isCustomConfigJson(value) {
1072
+ return typeof value === "object" && value !== null;
1073
+ }
1074
+ /**
1075
+ * @description 解析开发者工具自动预热相关配置的最终生效值。
1076
+ */
1077
+ function resolveDevtoolsAutomationDefaults(config) {
1078
+ return {
1079
+ autoBootstrapDevtools: config.autoBootstrapDevtools ?? true,
1080
+ autoTrustProject: config.autoTrustProject ?? false
1081
+ };
1082
+ }
1083
+ /**
1084
+ * @description 读取并解析 CLI 配置(进程选择优先,其次用户配置与平台默认)
1085
+ */
1086
+ async function getConfig() {
1087
+ const frozen = wechatDevtoolsSelectionContext.getStore();
1088
+ if (frozen) return {
1089
+ ...await readCustomConfig(),
1090
+ cliPath: frozen.cliPath,
1091
+ source: "environment"
1092
+ };
1093
+ if (process.env.WEAPP_IDE_DEVTOOLS_CHANNEL?.trim() || process.env.WEAPP_IDE_DEVTOOLS_VERSION?.trim()) {
1094
+ const target = await resolveWechatDevtoolsTarget();
1095
+ return {
1096
+ ...await readCustomConfig(),
1097
+ cliPath: target.cliPath,
1098
+ source: "environment"
1099
+ };
1100
+ }
1101
+ const selectedCliPath = process.env.WEAPP_IDE_CLI_PATH?.trim();
1102
+ if (await fs$1.pathExists(defaultCustomConfigFilePath)) try {
1103
+ const rawConfig = await fs$1.readJSON(defaultCustomConfigFilePath);
1104
+ const config = isCustomConfigJson(rawConfig) ? rawConfig : {};
1105
+ const cliPath = typeof config.cliPath === "string" ? config.cliPath.trim() : "";
1106
+ const locale = config.locale === "zh" || config.locale === "en" ? config.locale : void 0;
1107
+ const autoBootstrapDevtools = typeof config.autoBootstrapDevtools === "boolean" ? config.autoBootstrapDevtools : void 0;
1108
+ const autoTrustProject = typeof config.autoTrustProject === "boolean" ? config.autoTrustProject : void 0;
1109
+ if (selectedCliPath || cliPath) {
1110
+ logger.info(`全局配置文件路径:${colors.green(defaultCustomConfigFilePath)}`);
1111
+ logger.info(`${selectedCliPath ? "进程指定" : "自定义"} CLI 路径:${colors.green(selectedCliPath || cliPath)}`);
1112
+ return {
1113
+ cliPath: selectedCliPath || cliPath,
1114
+ locale,
1115
+ autoBootstrapDevtools,
1116
+ autoTrustProject,
1117
+ source: selectedCliPath ? "environment" : "custom"
1118
+ };
1119
+ }
1120
+ logger.warn("自定义配置文件缺少有效的 CLI 路径,将尝试使用默认路径。");
1121
+ } catch (error) {
1122
+ const reason = error instanceof Error ? error.message : String(error);
1123
+ logger.warn(`解析自定义配置失败,${selectedCliPath ? "继续使用进程指定路径" : "将尝试使用默认路径"}。原因:${reason}`);
1124
+ }
1125
+ if (selectedCliPath) return {
1126
+ cliPath: selectedCliPath,
1127
+ source: "environment"
1128
+ };
1129
+ const fallbackPath = await selectInstalledWechatDevtools().then((target) => target.cliPath, (error) => {
1130
+ if (error.code === "WECHAT_DEVTOOLS_INSTALLATION_NOT_FOUND") return;
1131
+ throw error;
1132
+ });
1133
+ if (fallbackPath) return {
1134
+ cliPath: fallbackPath,
1135
+ locale: void 0,
1136
+ autoBootstrapDevtools: void 0,
1137
+ autoTrustProject: void 0,
1138
+ source: "default"
1139
+ };
1140
+ return {
1141
+ cliPath: "",
1142
+ locale: void 0,
1143
+ autoBootstrapDevtools: void 0,
1144
+ autoTrustProject: void 0,
1145
+ source: "missing"
1146
+ };
1147
+ }
1148
+ /**
1149
+ * @description 获取用户配置的语言偏好。
1150
+ */
1151
+ async function getConfiguredLocale() {
1152
+ return (await readCustomConfig()).locale;
1153
+ }
1154
+ //#endregion
1155
+ //#region src/cli/prompt.ts
1156
+ /**
1157
+ * @description 交互式提示并保存 CLI 路径
1158
+ */
1159
+ async function promptForCliPath() {
1160
+ return await runWithSuspendedSharedInput(async () => {
1161
+ const rl = createInterface({
1162
+ input: stdin,
1163
+ output: stdout
1164
+ });
1165
+ try {
1166
+ logger.info(`请设置 ${colors.bold("微信web开发者工具 CLI")} 的路径`);
1167
+ logger.info("提示:命令行工具默认所在位置:");
1168
+ logger.info(`- MacOS: ${colors.green("<安装路径>/Contents/MacOS/cli")}`);
1169
+ logger.info(`- Windows: ${colors.green("<安装路径>/cli.bat")}`);
1170
+ logger.info(`- Linux: ${colors.green("<安装路径>/files/bin/bin/wechat-devtools-cli")}`);
1171
+ const cliPath = (await rl.question("请输入微信web开发者工具 CLI 路径:")).trim();
1172
+ if (!cliPath) {
1173
+ logger.error("路径不能为空,已取消本次配置。");
1174
+ return null;
1175
+ }
1176
+ try {
1177
+ const normalizedPath = await createCustomConfig({ cliPath });
1178
+ logger.info(`全局配置存储位置:${colors.green(defaultCustomConfigFilePath)}`);
1179
+ if (!await fs$1.pathExists(normalizedPath)) logger.warn("在当前路径未找到微信web开发者命令行工具,请确认路径是否正确。");
1180
+ return normalizedPath;
1181
+ } catch (error) {
1182
+ const reason = error instanceof Error ? error.message : String(error);
1183
+ logger.error(`保存配置失败:${reason}`);
1184
+ return null;
1185
+ }
1186
+ } finally {
1187
+ rl.close();
1188
+ }
1189
+ });
1190
+ }
1191
+ //#endregion
1192
+ //#region src/cli/resolver.ts
1193
+ /**
1194
+ * @description 解析 CLI 路径并校验可用性
1195
+ */
1196
+ async function resolveCliPath() {
1197
+ const config = await getConfig();
1198
+ if (!config.cliPath) return {
1199
+ cliPath: null,
1200
+ source: config.source
1201
+ };
1202
+ return {
1203
+ cliPath: await fs$1.pathExists(config.cliPath) ? config.cliPath : null,
1204
+ source: config.source
1205
+ };
1206
+ }
1207
+ //#endregion
881
1208
  //#region src/cli/config-command.ts
882
1209
  function parseBooleanConfigValue(rawValue) {
883
1210
  const normalized = rawValue.trim().toLowerCase();
@@ -898,13 +1225,13 @@ async function handleConfigCommand(argv) {
898
1225
  const nextLocale = argv[1];
899
1226
  if (nextLocale !== "zh" && nextLocale !== "en") throw new Error(i18nText("请使用 weapp config lang <zh|en> 切换语言", "Use weapp config lang <zh|en> to switch language"));
900
1227
  await createLocaleConfig(nextLocale);
901
- logger_default.info(i18nText(`语言已切换为:${nextLocale === "zh" ? "中文" : "英文"}`, `Language switched to: ${nextLocale}`));
1228
+ logger.info(i18nText(`语言已切换为:${nextLocale === "zh" ? "中文" : "英文"}`, `Language switched to: ${nextLocale}`));
902
1229
  return;
903
1230
  }
904
1231
  if (action === "show") {
905
1232
  const config = await readCustomConfig();
906
1233
  const effectiveDevtoolsDefaults = resolveDevtoolsAutomationDefaults(config);
907
- logger_default.info(i18nText(`配置文件路径:${colors.green(defaultCustomConfigFilePath)}`, `Config file: ${colors.green(defaultCustomConfigFilePath)}`));
1234
+ logger.info(i18nText(`配置文件路径:${colors.green(defaultCustomConfigFilePath)}`, `Config file: ${colors.green(defaultCustomConfigFilePath)}`));
908
1235
  console.log(JSON.stringify({
909
1236
  ...config,
910
1237
  effective: effectiveDevtoolsDefaults
@@ -932,25 +1259,25 @@ async function handleConfigCommand(argv) {
932
1259
  if (!value) throw new Error(i18nText("请提供配置值,例如:weapp config set locale en", "Please provide a value, e.g. weapp config set locale en"));
933
1260
  if (key === "cliPath") {
934
1261
  await createCustomConfig({ cliPath: value });
935
- logger_default.info(i18nText("CLI 路径已更新。", "CLI path updated."));
1262
+ logger.info(i18nText("CLI 路径已更新。", "CLI path updated."));
936
1263
  return;
937
1264
  }
938
1265
  if (key === "locale") {
939
1266
  if (value !== "zh" && value !== "en") throw new Error(i18nText("locale 仅支持 zh 或 en", "locale only supports zh or en"));
940
1267
  await createLocaleConfig(value);
941
- logger_default.info(i18nText(`语言已切换为:${value === "zh" ? "中文" : "英文"}`, `Language switched to: ${value}`));
1268
+ logger.info(i18nText(`语言已切换为:${value === "zh" ? "中文" : "英文"}`, `Language switched to: ${value}`));
942
1269
  return;
943
1270
  }
944
1271
  if (key === "autoBootstrapDevtools") {
945
1272
  const parsed = parseBooleanConfigValue(value);
946
1273
  await createAutoBootstrapDevtoolsConfig(parsed);
947
- logger_default.info(i18nText(`autoBootstrapDevtools 已更新为:${parsed}`, `autoBootstrapDevtools updated to: ${parsed}`));
1274
+ logger.info(i18nText(`autoBootstrapDevtools 已更新为:${parsed}`, `autoBootstrapDevtools updated to: ${parsed}`));
948
1275
  return;
949
1276
  }
950
1277
  if (key === "autoTrustProject") {
951
1278
  const parsed = parseBooleanConfigValue(value);
952
1279
  await createAutoTrustProjectConfig(parsed);
953
- logger_default.info(i18nText(`autoTrustProject 已更新为:${parsed}`, `autoTrustProject updated to: ${parsed}`));
1280
+ logger.info(i18nText(`autoTrustProject 已更新为:${parsed}`, `autoTrustProject updated to: ${parsed}`));
954
1281
  return;
955
1282
  }
956
1283
  throw new Error(i18nText("仅支持设置配置项:cliPath | locale | autoBootstrapDevtools | autoTrustProject", "Supported keys for set: cliPath | locale | autoBootstrapDevtools | autoTrustProject"));
@@ -959,7 +1286,7 @@ async function handleConfigCommand(argv) {
959
1286
  const key = argv[1];
960
1287
  if (key !== "cliPath" && key !== "locale" && key !== "autoBootstrapDevtools" && key !== "autoTrustProject") throw new Error(i18nText("仅支持清除配置项:cliPath | locale | autoBootstrapDevtools | autoTrustProject", "Supported keys for unset: cliPath | locale | autoBootstrapDevtools | autoTrustProject"));
961
1288
  await removeCustomConfigKey(key);
962
- logger_default.info(i18nText(`已清除配置项:${key}`, `Config key cleared: ${key}`));
1289
+ logger.info(i18nText(`已清除配置项:${key}`, `Config key cleared: ${key}`));
963
1290
  return;
964
1291
  }
965
1292
  if (action === "doctor") {
@@ -970,10 +1297,19 @@ async function handleConfigCommand(argv) {
970
1297
  const hasValidCli = Boolean(resolvedCli.cliPath);
971
1298
  const locale = rawConfig.locale ?? "zh";
972
1299
  const effectiveDevtoolsDefaults = resolveDevtoolsAutomationDefaults(rawConfig);
1300
+ const installation = resolvedCli.cliPath ? await resolveWechatDevtoolsTarget({ cliPath: resolvedCli.cliPath }).then((target) => ({
1301
+ status: "identified",
1302
+ installationId: target.installationId,
1303
+ version: target.version ?? null,
1304
+ channel: target.channel ?? "unknown",
1305
+ profileDir: target.profileDir
1306
+ }), () => ({ status: "unavailable" })) : { status: "unavailable" };
973
1307
  const report = {
974
1308
  configFile: defaultCustomConfigFilePath,
975
1309
  configFileExists: hasConfigFile,
976
1310
  cliPath: rawConfig.cliPath ?? null,
1311
+ effectiveCliPath: resolvedCli.cliPath,
1312
+ installation,
977
1313
  cliPathValid: hasValidCli,
978
1314
  locale,
979
1315
  autoBootstrapDevtools: rawConfig.autoBootstrapDevtools ?? null,
@@ -982,10 +1318,10 @@ async function handleConfigCommand(argv) {
982
1318
  effectiveAutoTrustProject: effectiveDevtoolsDefaults.autoTrustProject,
983
1319
  source: resolvedCli.source
984
1320
  };
985
- logger_default.info(i18nText("配置诊断结果:", "Configuration diagnostics:"));
1321
+ logger.info(i18nText("配置诊断结果:", "Configuration diagnostics:"));
986
1322
  console.log(JSON.stringify(report, null, 2));
987
- if (!hasConfigFile) logger_default.warn(i18nText("未找到配置文件,可执行 weapp config 进行初始化。", "Config file not found. Run `weapp config` to initialize."));
988
- if (hasCustomCli && !hasValidCli) logger_default.warn(i18nText("检测到配置的 cliPath 不可用,请执行 weapp config set cliPath <path> 修复。", "Configured cliPath is not available. Run `weapp config set cliPath <path>` to fix it."));
1323
+ if (!hasConfigFile) logger.warn(i18nText("未找到配置文件,可执行 weapp config 进行初始化。", "Config file not found. Run `weapp config` to initialize."));
1324
+ if (hasCustomCli && !hasValidCli) logger.warn(i18nText("检测到配置的 cliPath 不可用,请执行 weapp config set cliPath <path> 修复。", "Configured cliPath is not available. Run `weapp config set cliPath <path>` to fix it."));
989
1325
  return;
990
1326
  }
991
1327
  if (action === "export") {
@@ -996,7 +1332,7 @@ async function handleConfigCommand(argv) {
996
1332
  spaces: 2,
997
1333
  encoding: "utf8"
998
1334
  });
999
- logger_default.info(i18nText(`配置已导出到:${colors.green(outputPath)}`, `Config exported to: ${colors.green(outputPath)}`));
1335
+ logger.info(i18nText(`配置已导出到:${colors.green(outputPath)}`, `Config exported to: ${colors.green(outputPath)}`));
1000
1336
  return;
1001
1337
  }
1002
1338
  console.log(JSON.stringify(config, null, 2));
@@ -1018,12 +1354,341 @@ async function handleConfigCommand(argv) {
1018
1354
  autoBootstrapDevtools,
1019
1355
  autoTrustProject
1020
1356
  });
1021
- logger_default.info(i18nText(`配置已从 ${colors.green(inputPath)} 导入`, `Config imported from ${colors.green(inputPath)}`));
1357
+ logger.info(i18nText(`配置已从 ${colors.green(inputPath)} 导入`, `Config imported from ${colors.green(inputPath)}`));
1022
1358
  return;
1023
1359
  }
1024
1360
  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"));
1025
1361
  }
1026
1362
  //#endregion
1363
+ //#region src/cli/devtoolsSelection.ts
1364
+ const selectionOptions$1 = /* @__PURE__ */ new Map([
1365
+ ["--cli-path", "cliPath"],
1366
+ ["--devtools-channel", "devtoolsChannel"],
1367
+ ["--devtools-version", "devtoolsVersion"]
1368
+ ]);
1369
+ const valueOptions = /* @__PURE__ */ new Set([
1370
+ "--project",
1371
+ "-p",
1372
+ "--desc",
1373
+ "-d",
1374
+ "--version",
1375
+ "-v",
1376
+ "--uv",
1377
+ "--appid",
1378
+ "--ext-appid",
1379
+ "--info-output",
1380
+ "-i",
1381
+ "--result-output",
1382
+ "-r",
1383
+ "--qr-output",
1384
+ "--qr-format",
1385
+ "--qr-size",
1386
+ "--port",
1387
+ "--timeout",
1388
+ "-t",
1389
+ "--session-id",
1390
+ "--output",
1391
+ "-o",
1392
+ "--page",
1393
+ "--lang",
1394
+ "--platform",
1395
+ "--runtime-url",
1396
+ "--login-retry",
1397
+ "--login-retry-timeout",
1398
+ "--config",
1399
+ "-c",
1400
+ "--mode",
1401
+ "-m",
1402
+ "--filter",
1403
+ "-f",
1404
+ "--base",
1405
+ "--logLevel",
1406
+ "-l",
1407
+ "--project-config",
1408
+ "--logPath",
1409
+ "--ticket",
1410
+ "--account",
1411
+ "--test-ticket",
1412
+ "--compile-condition",
1413
+ "--compile-type",
1414
+ "--clean",
1415
+ "--auto-port",
1416
+ "--auto-account",
1417
+ "--replay-config-path",
1418
+ "--key-store",
1419
+ "--key-alias",
1420
+ "--key-pass",
1421
+ "--store-pass",
1422
+ "--use-aab",
1423
+ "--resourceBundleVersion",
1424
+ "--resourceBundleDesc",
1425
+ "--isDistribute",
1426
+ "--isRemoteBuild",
1427
+ "--profilePath",
1428
+ "--certificateName",
1429
+ "--p12Path",
1430
+ "--p12Password",
1431
+ "--tpnsProfilePath",
1432
+ "--isUploadBeta",
1433
+ "--versionName",
1434
+ "--versionCode",
1435
+ "--versionDesc"
1436
+ ]);
1437
+ /** 包装参数不会透传官方 CLI,选项值和双横线后的内容保持原样。 */
1438
+ function splitWechatDevtoolsSelectionArgs(argv) {
1439
+ const selection = {};
1440
+ const args = [];
1441
+ for (let index = 0; index < argv.length; index++) {
1442
+ const token = argv[index];
1443
+ if (token === "--") {
1444
+ args.push(...argv.slice(index));
1445
+ break;
1446
+ }
1447
+ const equals = token.indexOf("=");
1448
+ const name = equals < 0 ? token : token.slice(0, equals);
1449
+ const key = selectionOptions$1.get(name);
1450
+ if (key) {
1451
+ const value = equals < 0 ? argv[++index] : token.slice(equals + 1);
1452
+ if (!value?.trim() || value.startsWith("-")) throw new Error(`${name} requires a value.`);
1453
+ if (selection[key] !== void 0 && selection[key] !== value.trim()) throw new Error(`Conflicting ${name} values in the same command.`);
1454
+ Object.assign(selection, { [key]: value.trim() });
1455
+ } else if (name === "--require-official-latest") {
1456
+ const value = equals < 0 ? "true" : token.slice(equals + 1);
1457
+ if (![
1458
+ "true",
1459
+ "false",
1460
+ "1",
1461
+ "0"
1462
+ ].includes(value)) throw new Error("--require-official-latest requires a boolean.");
1463
+ const enabled = value === "true" || value === "1";
1464
+ if (selection.requireOfficialLatest !== void 0 && selection.requireOfficialLatest !== enabled) throw new Error("Conflicting --require-official-latest values in the same command.");
1465
+ selection.requireOfficialLatest = enabled;
1466
+ } else {
1467
+ args.push(token);
1468
+ if (equals < 0 && valueOptions.has(name) && argv[index + 1] !== void 0) args.push(argv[++index]);
1469
+ }
1470
+ }
1471
+ validateWechatDevtoolsSelection(selection);
1472
+ return {
1473
+ args,
1474
+ selection
1475
+ };
1476
+ }
1477
+ /** 显式选择在任何 helper 或 CLI 回退之前固定,避免入口间选版漂移。 */
1478
+ async function withWechatDevtoolsSelectionArgs(argv, runner) {
1479
+ const { args, selection } = splitWechatDevtoolsSelectionArgs(argv);
1480
+ return Object.keys(selection).length ? await withWechatDevtoolsSelection(selection, () => runner(args)) : await runner(args);
1481
+ }
1482
+ //#endregion
1483
+ //#region src/cli/managedProjectGate.ts
1484
+ function sameTarget(first, second) {
1485
+ 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;
1486
+ }
1487
+ async function findConfirmedProject(journalPath, target, projectPath) {
1488
+ const matching = (await withManagedJournalLock(journalPath, async (scopeRoot) => {
1489
+ const scopeRecords = await readManagedWechatProjectRecords(scopeRoot);
1490
+ if (scopeRecords.some((record) => [
1491
+ "starting",
1492
+ "unconfirmed",
1493
+ "closing",
1494
+ "failed"
1495
+ ].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.");
1496
+ return scopeRoot === journalPath ? scopeRecords : await readManagedWechatProjectRecords(journalPath);
1497
+ })).filter((record) => record.state !== "released" && record.projectPath === projectPath && sameTarget(record.target, target));
1498
+ for (const record of matching) try {
1499
+ if (!record.host || !record.port || (record.state === "owned" ? record.openedProjectWindow !== true : record.openedProjectWindow !== false)) throw new Error("Managed DevTools project has no confirmed window receipt and listener identity.");
1500
+ const live = await inspectManagedProjectHost(target, record.port);
1501
+ if (!sameManagedProcess(live, record.host)) throw new Error("Managed DevTools project listener changed; refusing to reopen the project.");
1502
+ if (matching.some((other) => other.port !== record.port || !other.host || !sameManagedProcess(other.host, record.host))) throw new Error("Managed DevTools project has conflicting listener records; refusing to restart its automator.");
1503
+ } catch (error) {
1504
+ await withManagedJournalLock(record.journalPath, async () => {
1505
+ const current = await readManagedRecord(record.journalPath, record.id);
1506
+ if (current.state === "released") return;
1507
+ current.state = "failed";
1508
+ current.error = error instanceof Error ? error.message : String(error);
1509
+ await writeManagedRecord(current);
1510
+ });
1511
+ throw error;
1512
+ }
1513
+ return matching[0];
1514
+ }
1515
+ /** 开窗与构建入口先确认任务持有的项目回执,已登记项目保留原自动化端口。 */
1516
+ async function ensureManagedWechatProject(target, projectPath, options = {}) {
1517
+ options.signal?.throwIfAborted();
1518
+ const journalPath = resolveManagedJournal();
1519
+ if (!journalPath) return;
1520
+ if (!projectPath.trim()) throw new Error("Managed DevTools project mutation requires an explicit project path.");
1521
+ return await withMachineE2ELease(async () => {
1522
+ const resolvedProjectPath = path.resolve(projectPath);
1523
+ const existing = await findConfirmedProject(journalPath, target, resolvedProjectPath);
1524
+ options.signal?.throwIfAborted();
1525
+ await assertManagedInstallation(target);
1526
+ options.signal?.throwIfAborted();
1527
+ if (existing) return existing;
1528
+ const { launchAutomator } = await import("./automator-VSjgHujY.js");
1529
+ await (await launchAutomator({
1530
+ target,
1531
+ cliPath: target.cliPath,
1532
+ projectPath: resolvedProjectPath,
1533
+ runtimeProvider: "devtools",
1534
+ preserveProjectRoot: true,
1535
+ persistAsDefaultSession: true,
1536
+ signal: options.signal,
1537
+ trustProject: options.trustProject,
1538
+ timeout: 12e4
1539
+ })).disconnect();
1540
+ options.signal?.throwIfAborted();
1541
+ const confirmed = await findConfirmedProject(journalPath, target, resolvedProjectPath);
1542
+ if (!confirmed) throw new Error("Managed DevTools launcher returned without a confirmed task project; refusing a project mutation.");
1543
+ return confirmed;
1544
+ });
1545
+ }
1546
+ //#endregion
1547
+ //#region src/cli/wechatDevtoolsRuntimePort.ts
1548
+ const runtimePorts = /* @__PURE__ */ new Map();
1549
+ /**
1550
+ * @description 标记所选安装检测到的服务端口;无身份的旧调用仅保留在独立兼容槽中。
1551
+ */
1552
+ function setRuntimeWechatDevtoolsServicePort(port, target) {
1553
+ const key = target?.installationId ?? "";
1554
+ if (typeof port !== "number" || !Number.isInteger(port) || port <= 0 || port > 65535) {
1555
+ runtimePorts.delete(key);
1556
+ return;
1557
+ }
1558
+ runtimePorts.set(key, port);
1559
+ }
1560
+ /**
1561
+ * @description 只读取所选安装的服务端口,不借用其他安装或无身份的旧记录。
1562
+ */
1563
+ function getRuntimeWechatDevtoolsServicePort(target) {
1564
+ return runtimePorts.get(target?.installationId ?? "");
1565
+ }
1566
+ //#endregion
1567
+ //#region src/cli/http.ts
1568
+ const ENGINE_BUILD_NOT_START = "NOT_START";
1569
+ const ENGINE_BUILD_OPEN_PROJECT = "OPEN_PROJECT";
1570
+ const ENGINE_BUILD_BUILDING = "BUILDING";
1571
+ const ENGINE_BUILD_END = "END";
1572
+ const ENGINE_BUILD_ERROR = "ERROR";
1573
+ function createWechatDevtoolsHttpError(message, code) {
1574
+ const error = new Error(message);
1575
+ error.code = code;
1576
+ return error;
1577
+ }
1578
+ async function resolveWechatDevtoolsHttpPort(target, port) {
1579
+ if (port !== void 0) {
1580
+ if (typeof port !== "number" || !Number.isInteger(port) || port < 1 || port > 65535) throw createWechatDevtoolsHttpError("The explicit DevTools HTTP port must be an integer from 1 to 65535.", "WECHAT_DEVTOOLS_INVALID_PORT");
1581
+ return port;
1582
+ }
1583
+ const runtimePort = getRuntimeWechatDevtoolsServicePort(target);
1584
+ if (runtimePort) return runtimePort;
1585
+ const detected = await detectWechatDevtoolsServicePort({ target });
1586
+ if (detected.servicePortEnabled === false) throw createWechatDevtoolsHttpError("WECHAT_DEVTOOLS_SERVICE_PORT_DISABLED", "WECHAT_DEVTOOLS_SERVICE_PORT_DISABLED");
1587
+ if (!detected.servicePort) throw createWechatDevtoolsHttpError("The selected DevTools installation has no known HTTP service port.", "WECHAT_DEVTOOLS_SERVICE_PORT_UNKNOWN");
1588
+ return detected.servicePort;
1589
+ }
1590
+ function createWechatDevtoolsHttpUrl(port, pathname, query) {
1591
+ const url = new URL(`http://127.0.0.1:${port}${pathname}`);
1592
+ for (const [key, value] of Object.entries(query)) url.searchParams.set(key, value);
1593
+ return url;
1594
+ }
1595
+ function parseWechatDevtoolsEngineBuildResult(body) {
1596
+ try {
1597
+ return JSON.parse(body);
1598
+ } catch {
1599
+ return {};
1600
+ }
1601
+ }
1602
+ async function requestSelectedWechatDevtoolsHttp(pathname, query, options = {}) {
1603
+ options.signal?.throwIfAborted();
1604
+ const target = await resolveWechatDevtoolsTarget(options);
1605
+ options.signal?.throwIfAborted();
1606
+ const projectParameter = pathname === "/v2/open" ? "project" : pathname === "/engine/build" ? "projectpath" : void 0;
1607
+ if (projectParameter) {
1608
+ await ensureManagedWechatProject(target, query[projectParameter] ?? "", { signal: options.signal });
1609
+ options.signal?.throwIfAborted();
1610
+ }
1611
+ const port = await resolveWechatDevtoolsHttpPort(target, options.port).catch((error) => {
1612
+ options.signal?.throwIfAborted();
1613
+ throw error;
1614
+ });
1615
+ options.signal?.throwIfAborted();
1616
+ await assertWechatDevtoolsPort(target, port, {
1617
+ signal: options.signal,
1618
+ timeout: options.timeoutMs
1619
+ });
1620
+ options.signal?.throwIfAborted();
1621
+ const url = createWechatDevtoolsHttpUrl(port, pathname, query);
1622
+ const controller = new AbortController();
1623
+ const signal = options.signal ? AbortSignal.any([options.signal, controller.signal]) : controller.signal;
1624
+ const timeout = setTimeout(() => {
1625
+ controller.abort(createWechatDevtoolsHttpError("WECHAT_DEVTOOLS_HTTP_TIMEOUT", "WECHAT_DEVTOOLS_HTTP_TIMEOUT"));
1626
+ }, options.timeoutMs ?? 1e4);
1627
+ try {
1628
+ const response = await fetch(url, {
1629
+ method: "GET",
1630
+ signal
1631
+ });
1632
+ const body = await response.text();
1633
+ signal.throwIfAborted();
1634
+ if (!response.ok) throw createWechatDevtoolsHttpError(body || `HTTP ${response.status}`, "WECHAT_DEVTOOLS_HTTP_REQUEST_FAILED");
1635
+ return body;
1636
+ } catch (error) {
1637
+ signal.throwIfAborted();
1638
+ throw error;
1639
+ } finally {
1640
+ clearTimeout(timeout);
1641
+ }
1642
+ }
1643
+ /** 请求期间持有机器租约,避免 E2E 与另一入口同时更改共享宿主。 */
1644
+ async function requestWechatDevtoolsHttp(pathname, query, options = {}) {
1645
+ options.signal?.throwIfAborted();
1646
+ return await withMachineE2ELease(() => requestSelectedWechatDevtoolsHttp(pathname, query, options));
1647
+ }
1648
+ /**
1649
+ * @description 请求微信开发者工具打开项目并返回原始响应;目标 runtime 状态仍需通过会话或界面确认。
1650
+ */
1651
+ async function openWechatIdeProjectByHttp(projectPath, options = {}) {
1652
+ const body = await requestWechatDevtoolsHttp("/v2/open", { project: path.resolve(projectPath) }, options);
1653
+ const response = body.trim();
1654
+ if (!response || /^\{\s*\}$/.test(response)) throw createWechatDevtoolsHttpError("DevTools HTTP returned no project-open result; the target project opening is unconfirmed.", "WECHAT_DEVTOOLS_HTTP_OPEN_UNCONFIRMED");
1655
+ return body;
1656
+ }
1657
+ /**
1658
+ * @description 通过微信开发者工具 HTTP 服务端口重置当前项目的 fileutils 状态。
1659
+ */
1660
+ async function resetWechatIdeFileUtilsByHttp(projectPath, options = {}) {
1661
+ return await requestWechatDevtoolsHttp("/v2/resetfileutils", { project: path.resolve(projectPath) }, options);
1662
+ }
1663
+ /**
1664
+ * @description 通过微信开发者工具 HTTP 服务端口触发 engine build。
1665
+ */
1666
+ async function startWechatIdeEngineBuildByHttp(projectPath, options = {}) {
1667
+ return { body: await requestWechatDevtoolsHttp("/engine/build", { projectpath: path.resolve(projectPath) }, options) };
1668
+ }
1669
+ /**
1670
+ * @description 轮询微信开发者工具 engine build 状态。
1671
+ */
1672
+ async function pollWechatIdeEngineBuildResultByHttp(options = {}) {
1673
+ const body = await requestWechatDevtoolsHttp("/engine/buildResult/", {}, options);
1674
+ const parsed = parseWechatDevtoolsEngineBuildResult(body);
1675
+ const status = parsed.status;
1676
+ return {
1677
+ body,
1678
+ done: status === ENGINE_BUILD_END,
1679
+ failed: status === ENGINE_BUILD_ERROR,
1680
+ msg: parsed.msg,
1681
+ status
1682
+ };
1683
+ }
1684
+ const WECHAT_DEVTOOLS_ENGINE_BUILD_STATUSES = {
1685
+ BUILDING: ENGINE_BUILD_BUILDING,
1686
+ END: ENGINE_BUILD_END,
1687
+ ERROR: ENGINE_BUILD_ERROR,
1688
+ NOT_START: ENGINE_BUILD_NOT_START,
1689
+ OPEN_PROJECT: ENGINE_BUILD_OPEN_PROJECT
1690
+ };
1691
+ //#endregion
1027
1692
  //#region src/cli/engine.ts
1028
1693
  function createEngineBuildError(message, code) {
1029
1694
  const error = new Error(message);
@@ -1089,13 +1754,16 @@ function compactOutput(value) {
1089
1754
  }
1090
1755
  async function runWechatIdeEngineBuildByCli(projectPath, options = {}) {
1091
1756
  options.signal?.throwIfAborted();
1092
- const { cliPath } = await resolveCliPath().catch((error) => {
1757
+ const target = await resolveWechatDevtoolsTarget(options).catch((error) => {
1093
1758
  options.signal?.throwIfAborted();
1094
1759
  throw error;
1095
1760
  });
1096
1761
  options.signal?.throwIfAborted();
1097
- if (!cliPath) throw createEngineBuildError("WECHAT_DEVTOOLS_CLI_NOT_FOUND", "WECHAT_DEVTOOLS_CLI_NOT_FOUND");
1098
- const result = await execa(cliPath, [
1762
+ await ensureManagedWechatProject(target, projectPath, { signal: options.signal });
1763
+ options.signal?.throwIfAborted();
1764
+ await assertWechatDevtoolsHost(target, { signal: options.signal });
1765
+ options.signal?.throwIfAborted();
1766
+ const result = await execa(target.cliPath, [
1099
1767
  "engine",
1100
1768
  "build",
1101
1769
  path.resolve(projectPath)
@@ -1115,20 +1783,20 @@ async function runWechatIdeEngineBuildByCli(projectPath, options = {}) {
1115
1783
  await writeEngineBuildLog(options.logPath, output);
1116
1784
  options.signal?.throwIfAborted();
1117
1785
  if ((result.exitCode ?? 1) === 0) {
1118
- if (stdout) process.stdout.write(stdout);
1119
- if (stderr) process.stderr.write(stderr);
1786
+ if (stdout && !options.quiet) process.stdout.write(stdout);
1787
+ if (stderr && !options.quiet) process.stderr.write(stderr);
1120
1788
  return;
1121
1789
  }
1122
1790
  if (ENGINE_BUILD_CLI_OPENED_PATTERN.test(output)) return;
1123
1791
  if (isEngineBuildEndpointMissingError(output)) throw createEngineBuildEndpointMissingError();
1124
- if (stdout) process.stdout.write(stdout);
1125
- if (stderr) process.stderr.write(stderr);
1792
+ if (stdout && !options.quiet) process.stdout.write(stdout);
1793
+ if (stderr && !options.quiet) process.stderr.write(stderr);
1126
1794
  throw createEngineBuildError(compactOutput(stderr) || compactOutput(stdout) || `WECHAT_DEVTOOLS_ENGINE_BUILD_CLI_FAILED:${result.exitCode ?? 1}`, "WECHAT_DEVTOOLS_ENGINE_BUILD_CLI_FAILED");
1127
1795
  }
1128
1796
  /**
1129
1797
  * @description 通过开发者工具 HTTP 服务端口执行 engine build,并轮询直到构建结束。
1130
1798
  */
1131
- async function runWechatIdeEngineBuildByHttp(projectPath, options = {}) {
1799
+ async function runSelectedWechatIdeEngineBuildByHttp(projectPath, options = {}) {
1132
1800
  options.signal?.throwIfAborted();
1133
1801
  await startWechatIdeEngineBuildByHttp(projectPath, options);
1134
1802
  const startedAt = Date.now();
@@ -1147,11 +1815,11 @@ async function runWechatIdeEngineBuildByHttp(projectPath, options = {}) {
1147
1815
  /**
1148
1816
  * @description 以更接近官方 CLI 的方式执行 engine build,并支持将构建日志写入文件。
1149
1817
  */
1150
- async function runWechatIdeEngineBuild(projectPath, options = {}) {
1818
+ async function runSelectedWechatIdeEngineBuild(projectPath, options = {}) {
1151
1819
  const logs = [];
1152
1820
  let lastLoggedMessage;
1153
1821
  try {
1154
- const result = await runWechatIdeEngineBuildByHttp(projectPath, {
1822
+ const result = await runSelectedWechatIdeEngineBuildByHttp(projectPath, {
1155
1823
  ...options,
1156
1824
  onProgress: (progress) => {
1157
1825
  if (progress.msg && progress.msg !== lastLoggedMessage) {
@@ -1175,6 +1843,91 @@ async function runWechatIdeEngineBuild(projectPath, options = {}) {
1175
1843
  throw error;
1176
1844
  }
1177
1845
  }
1846
+ /** 固定安装上下文后完整持有 HTTP 构建与轮询期间的租约。 */
1847
+ async function runWechatIdeEngineBuildByHttp(projectPath, options = {}) {
1848
+ options.signal?.throwIfAborted();
1849
+ return await withMachineE2ELease(async () => {
1850
+ const target = await resolveWechatDevtoolsTarget(options);
1851
+ options.signal?.throwIfAborted();
1852
+ return await runSelectedWechatIdeEngineBuildByHttp(projectPath, {
1853
+ ...options,
1854
+ target
1855
+ });
1856
+ });
1857
+ }
1858
+ /** HTTP、轮询与 CLI 回退沿用同一次解析的安装。 */
1859
+ async function runWechatIdeEngineBuild(projectPath, options = {}) {
1860
+ options.signal?.throwIfAborted();
1861
+ return await withMachineE2ELease(async () => {
1862
+ const target = await resolveWechatDevtoolsTarget(options);
1863
+ options.signal?.throwIfAborted();
1864
+ return await runSelectedWechatIdeEngineBuild(projectPath, {
1865
+ ...options,
1866
+ target
1867
+ });
1868
+ });
1869
+ }
1870
+ async function prepareSelectedAcceptanceProject(projectPath, signal, options) {
1871
+ await openWechatIdeProjectByHttp(projectPath, {
1872
+ ...options,
1873
+ signal,
1874
+ timeoutMs: 1e4
1875
+ });
1876
+ await sleep$1(1e3, signal);
1877
+ await resetWechatIdeFileUtilsByHttp(projectPath, {
1878
+ ...options,
1879
+ signal,
1880
+ timeoutMs: 1e4
1881
+ });
1882
+ const result = await runWechatIdeEngineBuild(projectPath, {
1883
+ ...options,
1884
+ signal,
1885
+ overallTimeoutMs: 6e4,
1886
+ timeoutMs: 1e4,
1887
+ quiet: true
1888
+ });
1889
+ if (!result) await sleep$1(1500, signal);
1890
+ return result;
1891
+ }
1892
+ /** 在验收交互前等待 IDE 消费产物,并沿用该会话选择的安装。 */
1893
+ async function prepareAcceptanceProject(projectPath, signal, options = {}) {
1894
+ signal.throwIfAborted();
1895
+ if (options.runtimeProvider === "headless") return;
1896
+ return await withMachineE2ELease(async () => {
1897
+ const target = await resolveWechatDevtoolsTarget(options);
1898
+ signal.throwIfAborted();
1899
+ await ensureManagedWechatProject(target, projectPath, { signal });
1900
+ signal.throwIfAborted();
1901
+ return await prepareSelectedAcceptanceProject(projectPath, signal, {
1902
+ ...options,
1903
+ target
1904
+ });
1905
+ });
1906
+ }
1907
+ //#endregion
1908
+ //#region src/cli/forwardConsole/refresh.ts
1909
+ /** 刷新已有项目的日志开关,并保留日志启用与连接释放的独立错误。 */
1910
+ async function refreshOpenedMiniProgramConsoleLog(options, enableLog) {
1911
+ const miniProgram = await connectConsoleMiniProgram({
1912
+ ...options,
1913
+ openedOnly: true
1914
+ });
1915
+ let failure;
1916
+ let failed = false;
1917
+ try {
1918
+ await enableLog(miniProgram);
1919
+ } catch (error) {
1920
+ failed = true;
1921
+ failure = error;
1922
+ }
1923
+ try {
1924
+ await miniProgram.disconnect();
1925
+ } catch (cleanupError) {
1926
+ if (failed) throw new AggregateError([failure, cleanupError], "Console log refresh and connection cleanup failed", { cause: failure });
1927
+ throw cleanupError;
1928
+ }
1929
+ if (failed) throw failure;
1930
+ }
1178
1931
  //#endregion
1179
1932
  //#region src/cli/forwardConsole.ts
1180
1933
  const DEFAULT_FORWARD_CONSOLE_LEVELS = [
@@ -1225,15 +1978,15 @@ function printForwardConsoleEvent(event) {
1225
1978
  const line = `${formatForwardConsolePrefix(event.level)} ${formatForwardConsoleMessage(event)}`;
1226
1979
  switch (event.level) {
1227
1980
  case "error":
1228
- logger_default.error(line);
1981
+ logger.error(line);
1229
1982
  return;
1230
1983
  case "warn":
1231
- logger_default.warn(line);
1984
+ logger.warn(line);
1232
1985
  return;
1233
1986
  case "info":
1234
- logger_default.info(line);
1987
+ logger.info(line);
1235
1988
  return;
1236
- default: logger_default.log(line);
1989
+ default: logger.log(line);
1237
1990
  }
1238
1991
  }
1239
1992
  function formatConsoleArgument(value) {
@@ -1319,29 +2072,28 @@ async function acquireForwardConsoleMiniProgram(options) {
1319
2072
  return miniProgram;
1320
2073
  } catch (error) {
1321
2074
  lastError = error;
1322
- if (miniProgram) releaseSharedMiniProgram(options.projectPath, options.sessionId || options.port);
1323
- await closeSharedMiniProgram(options.projectPath, options.sessionId || options.port);
2075
+ const cleanupErrors = [];
2076
+ if (miniProgram) try {
2077
+ await releaseSharedMiniProgram(options.projectPath, options.sessionId || options.port, options);
2078
+ } catch (cleanupError) {
2079
+ cleanupErrors.push(cleanupError);
2080
+ }
2081
+ try {
2082
+ await closeSharedMiniProgram(options.projectPath, options.sessionId || options.port, options);
2083
+ } catch (cleanupError) {
2084
+ cleanupErrors.push(cleanupError);
2085
+ }
2086
+ if (cleanupErrors.length) throw new AggregateError([error, ...cleanupErrors], "Forward console setup and connection cleanup failed", { cause: error });
1324
2087
  if (attempt < ENABLE_LOG_RETRY_TIMES) await sleep(ENABLE_LOG_RETRY_DELAY_MS);
1325
2088
  }
1326
2089
  }
1327
2090
  throw lastError instanceof Error ? lastError : new Error(String(lastError));
1328
2091
  }
1329
- async function refreshOpenedMiniProgramConsoleLog(options) {
1330
- const miniProgram = await connectConsoleMiniProgram({
1331
- ...options,
1332
- openedOnly: true
1333
- });
1334
- try {
1335
- await enableMiniProgramConsoleLog(miniProgram);
1336
- } finally {
1337
- miniProgram.disconnect();
1338
- }
1339
- }
1340
2092
  function startEnableLogRefresh(miniProgram, options, refreshOpenedSession) {
1341
2093
  if (typeof miniProgram.enableLog !== "function") return;
1342
2094
  const timer = setInterval(() => {
1343
2095
  miniProgram.enableLog(ENABLE_LOG_TIMEOUT_MS).catch(() => {});
1344
- if (refreshOpenedSession) refreshOpenedMiniProgramConsoleLog(options).catch(() => {});
2096
+ if (refreshOpenedSession) refreshOpenedMiniProgramConsoleLog(options, enableMiniProgramConsoleLog).catch(() => {});
1345
2097
  }, ENABLE_LOG_REFRESH_INTERVAL_MS);
1346
2098
  return () => {
1347
2099
  clearInterval(timer);
@@ -1351,6 +2103,7 @@ function startEnableLogRefresh(miniProgram, options, refreshOpenedSession) {
1351
2103
  * @description 启动小程序控制台日志转发,并保持 automator 会话常驻。
1352
2104
  */
1353
2105
  async function startForwardConsole(options) {
2106
+ options = options.miniProgram ? options : await resolveAutomatorSessionOptions(options);
1354
2107
  const usesSharedSession = !options.miniProgram;
1355
2108
  const miniProgram = await acquireForwardConsoleMiniProgram(options);
1356
2109
  const logLevels = new Set(options.logLevels?.length ? options.logLevels : DEFAULT_FORWARD_CONSOLE_LEVELS);
@@ -1376,8 +2129,10 @@ async function startForwardConsole(options) {
1376
2129
  let stopEnableLogRefresh;
1377
2130
  let auxiliaryMiniProgram;
1378
2131
  let auxiliaryTimer;
1379
- const closeSession = async () => {
1380
- if (closed) return;
2132
+ let closing;
2133
+ let sharedReleased = false;
2134
+ const closeSession = () => {
2135
+ if (closing) return closing;
1381
2136
  closed = true;
1382
2137
  if (auxiliaryTimer) {
1383
2138
  clearTimeout(auxiliaryTimer);
@@ -1387,13 +2142,33 @@ async function startForwardConsole(options) {
1387
2142
  stopEnableLogRefresh = void 0;
1388
2143
  detachMiniProgramListener(miniProgram, "console", onConsole);
1389
2144
  detachMiniProgramListener(miniProgram, "exception", onException);
1390
- if (auxiliaryMiniProgram) {
1391
- detachMiniProgramListener(auxiliaryMiniProgram, "console", onConsole);
1392
- detachMiniProgramListener(auxiliaryMiniProgram, "exception", onException);
1393
- auxiliaryMiniProgram.disconnect();
1394
- auxiliaryMiniProgram = void 0;
1395
- }
1396
- if (usesSharedSession) releaseSharedMiniProgram(options.projectPath, options.sessionId || options.port);
2145
+ closing = (async () => {
2146
+ const errors = [];
2147
+ if (auxiliaryMiniProgram) {
2148
+ detachMiniProgramListener(auxiliaryMiniProgram, "console", onConsole);
2149
+ detachMiniProgramListener(auxiliaryMiniProgram, "exception", onException);
2150
+ try {
2151
+ await auxiliaryMiniProgram.disconnect();
2152
+ auxiliaryMiniProgram = void 0;
2153
+ } catch (error) {
2154
+ errors.push(error);
2155
+ }
2156
+ }
2157
+ if (usesSharedSession) try {
2158
+ if (!sharedReleased) {
2159
+ sharedReleased = true;
2160
+ await releaseSharedMiniProgram(options.projectPath, options.sessionId || options.port, options);
2161
+ }
2162
+ await closeOwnedSharedMiniProgram(options, miniProgram);
2163
+ } catch (error) {
2164
+ errors.push(error);
2165
+ }
2166
+ if (errors.length) throw new AggregateError(errors, "Forward console session cleanup failed");
2167
+ })().catch((error) => {
2168
+ closing = void 0;
2169
+ throw error;
2170
+ });
2171
+ return closing;
1397
2172
  };
1398
2173
  miniProgram.on("console", onConsole);
1399
2174
  miniProgram.on("exception", onException);
@@ -1405,7 +2180,7 @@ async function startForwardConsole(options) {
1405
2180
  openedOnly: true
1406
2181
  });
1407
2182
  if (closed) {
1408
- openedMiniProgram.disconnect();
2183
+ await openedMiniProgram.disconnect();
1409
2184
  return;
1410
2185
  }
1411
2186
  auxiliaryMiniProgram = openedMiniProgram;
@@ -1418,6 +2193,117 @@ async function startForwardConsole(options) {
1418
2193
  return { close: closeSession };
1419
2194
  }
1420
2195
  //#endregion
2196
+ //#region src/utils/argv.ts
2197
+ function ensurePathArgument(argv, optionIndex) {
2198
+ const paramIdx = optionIndex + 1;
2199
+ const param = argv[paramIdx];
2200
+ if (param && !param.startsWith("-")) argv[paramIdx] = resolvePath(param);
2201
+ else argv.splice(paramIdx, 0, process.cwd());
2202
+ return argv;
2203
+ }
2204
+ /**
2205
+ * @description 依次应用 argv 处理函数(不修改原始 argv)
2206
+ */
2207
+ function transformArgv(argv, transforms) {
2208
+ return transforms.reduce((current, transform) => transform(current), [...argv]);
2209
+ }
2210
+ /**
2211
+ * @description 创建参数别名转换器
2212
+ */
2213
+ function createAlias(entry) {
2214
+ return (input) => {
2215
+ const argv = [...input];
2216
+ let optionIndex = argv.indexOf(entry.find);
2217
+ if (optionIndex > -1) argv.splice(optionIndex, 1, entry.replacement);
2218
+ else optionIndex = argv.indexOf(entry.replacement);
2219
+ if (optionIndex === -1) return argv;
2220
+ return ensurePathArgument(argv, optionIndex);
2221
+ };
2222
+ }
2223
+ /**
2224
+ * @description 创建路径参数兼容转换器(补全或规范化路径)
2225
+ */
2226
+ function createPathCompat(option) {
2227
+ return (input) => {
2228
+ const argv = [...input];
2229
+ const optionIndex = argv.indexOf(option);
2230
+ if (optionIndex === -1) return argv;
2231
+ return ensurePathArgument(argv, optionIndex);
2232
+ };
2233
+ }
2234
+ //#endregion
2235
+ //#region src/utils/exec.ts
2236
+ /**
2237
+ * @description 执行 CLI 命令并透传输出
2238
+ */
2239
+ async function execute(cliPath, argv, options = {}) {
2240
+ const { pipeStdout = true, pipeStderr = true, timeout } = options;
2241
+ const { execa } = await import("execa");
2242
+ const task = execa(cliPath, argv, {
2243
+ timeout,
2244
+ cancelSignal: options.signal,
2245
+ forceKillAfterDelay: 100
2246
+ });
2247
+ if (pipeStdout) task?.stdout?.pipe(process.stdout);
2248
+ if (pipeStderr) task?.stderr?.pipe(process.stderr);
2249
+ return await task;
2250
+ }
2251
+ //#endregion
2252
+ //#region src/cli/loginQuery.ts
2253
+ /** 读取原生 CLI 的明确登录结果;日志、空输出与相互矛盾的响应均不能证明登录状态。 */
2254
+ function parseLoginOutput(stdout) {
2255
+ const login = readWechatLoginState(stdout);
2256
+ if (login !== void 0) return {
2257
+ status: "success",
2258
+ login
2259
+ };
2260
+ return {
2261
+ status: "unknown",
2262
+ reason: "invalid-response"
2263
+ };
2264
+ }
2265
+ /** 显式调用指定 CLI 查询登录语义,不重试、不提示登录;原生 CLI 可能启动 IDE。 */
2266
+ async function queryWechatIdeLogin(cliPath, options = {}) {
2267
+ const timeout = options.timeout ?? 3e3;
2268
+ if (!Number.isFinite(timeout) || timeout <= 0) throw new TypeError("登录查询 timeout 必须是有限正数。");
2269
+ const operation = new OperationLifecycle(timeout, "IDE login query", options.signal);
2270
+ try {
2271
+ return await operation.run((scope) => withMachineE2ELease(async () => {
2272
+ const target = await scope.step(() => resolveWechatDevtoolsTarget({ cliPath }), { stage: "installation" });
2273
+ await scope.step(() => assertWechatDevtoolsHost(target, {
2274
+ signal: scope.signal,
2275
+ timeout: scope.remainingMs()
2276
+ }), {
2277
+ stage: "host-identity",
2278
+ waitForExit: true
2279
+ });
2280
+ return parseLoginOutput((await scope.step(() => execute(target.cliPath, ["islogin"], {
2281
+ pipeStdout: false,
2282
+ pipeStderr: false,
2283
+ timeout: scope.remainingMs(),
2284
+ signal: scope.signal
2285
+ }), {
2286
+ stage: "login-query",
2287
+ waitForExit: true
2288
+ })).stdout);
2289
+ }));
2290
+ } catch (error) {
2291
+ options.signal?.throwIfAborted();
2292
+ if (error instanceof Error && error.message.startsWith("Runtime busy:")) return {
2293
+ status: "unknown",
2294
+ reason: "runtime-busy"
2295
+ };
2296
+ if (error && typeof error === "object" && "code" in error && error.code === "WECHAT_DEVTOOLS_HOST_IDENTITY_MISMATCH") return {
2297
+ status: "unknown",
2298
+ reason: "installation-mismatch"
2299
+ };
2300
+ return {
2301
+ status: "unknown",
2302
+ reason: operation.timedOut || error && typeof error === "object" && "timedOut" in error && error.timedOut === true ? "timeout" : "command-failed"
2303
+ };
2304
+ }
2305
+ }
2306
+ //#endregion
1421
2307
  //#region src/cli/minidev.ts
1422
2308
  const MINIDEV_COMMAND = "minidev";
1423
2309
  function isCommandNotFound(error) {
@@ -1431,17 +2317,265 @@ async function runMinidev(argv) {
1431
2317
  await execute(MINIDEV_COMMAND, [...argv]);
1432
2318
  } catch (error) {
1433
2319
  if (isCommandNotFound(error)) {
1434
- logger_default.error("未检测到支付宝小程序 CLI:minidev");
1435
- logger_default.warn("请先安装 minidev,可使用以下任一命令:");
1436
- logger_default.info(`- ${colors.green("pnpm add -g minidev")}`);
1437
- logger_default.info(`- ${colors.green("npm install -g minidev")}`);
1438
- logger_default.info(`- ${colors.green("yarn global add minidev")}`);
2320
+ logger.error("未检测到支付宝小程序 CLI:minidev");
2321
+ logger.warn("请先安装 minidev,可使用以下任一命令:");
2322
+ logger.info(`- ${colors.green("pnpm add -g minidev")}`);
2323
+ logger.info(`- ${colors.green("npm install -g minidev")}`);
2324
+ logger.info(`- ${colors.green("yarn global add minidev")}`);
1439
2325
  return;
1440
2326
  }
1441
2327
  throw error;
1442
2328
  }
1443
2329
  }
1444
2330
  //#endregion
2331
+ //#region src/cli/devtools.ts
2332
+ /** 本地安装管理不启动 IDE,不更改账号或默认 CLI 配置。 */
2333
+ async function handleDevtoolsCommand(argv) {
2334
+ const [action = "list", cliPath] = argv;
2335
+ if (action === "list") {
2336
+ const installations = await listWechatDevtoolsInstallations();
2337
+ if (argv.includes("--json")) console.log(JSON.stringify({ installations }, null, 2));
2338
+ 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}`);
2339
+ return;
2340
+ }
2341
+ if (action !== "add" && action !== "remove") throw new Error("Use weapp devtools list [--json] | add <path-to-cli> | remove <path-to-cli>.");
2342
+ if (!cliPath || argv.length !== 2) throw new Error(`weapp devtools ${action} requires one CLI path.`);
2343
+ if (action === "add") console.log(JSON.stringify(await registerWechatDevtoolsInstallation(cliPath), null, 2));
2344
+ else await unregisterWechatDevtoolsInstallation(cliPath);
2345
+ }
2346
+ //#endregion
2347
+ //#region src/cli/run-login-config.ts
2348
+ function normalizeLoginRetryMode(value, nonInteractive) {
2349
+ 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)`));
2350
+ if (value === "never" || value === "once" || value === "always") return value;
2351
+ return nonInteractive ? "never" : "always";
2352
+ }
2353
+ function normalizeLoginRetryTimeout(value) {
2354
+ if (!value) return 3e4;
2355
+ const parsed = Number.parseInt(value, 10);
2356
+ if (!Number.isFinite(parsed) || parsed <= 0) throw new Error(i18nText(`无效的 --login-retry-timeout 值: ${value}(必须为正整数)`, `Invalid --login-retry-timeout value: ${value} (must be a positive integer)`));
2357
+ return parsed;
2358
+ }
2359
+ function isStdinInteractive() {
2360
+ return Boolean(process.stdin && process.stdin.isTTY);
2361
+ }
2362
+ function stripLoginRetryControlFlags(argv) {
2363
+ let next = removeOption(argv, "--login-retry");
2364
+ next = removeOption(next, "--login-retry-timeout");
2365
+ next = removeOption(next, "--non-interactive");
2366
+ return next;
2367
+ }
2368
+ /**
2369
+ * @description 解析登录重试相关控制参数,并产出实际执行微信 CLI 所需的运行时参数。
2370
+ */
2371
+ function resolveLoginRetryConfig(argv) {
2372
+ const nonInteractiveFlag = argv.includes("--non-interactive");
2373
+ const ciMode = process.env.CI === "true";
2374
+ const nonTtyStdin = !isStdinInteractive();
2375
+ const nonInteractive = nonInteractiveFlag || ciMode || nonTtyStdin;
2376
+ const retryModeRaw = readOptionValue(argv, "--login-retry")?.toLowerCase();
2377
+ return {
2378
+ nonInteractive,
2379
+ retryMode: normalizeLoginRetryMode(retryModeRaw, nonInteractive),
2380
+ retryTimeoutMs: normalizeLoginRetryTimeout(readOptionValue(argv, "--login-retry-timeout")),
2381
+ runtimeArgv: stripLoginRetryControlFlags(argv)
2382
+ };
2383
+ }
2384
+ //#endregion
2385
+ //#region src/cli/run-login.ts
2386
+ const IDE_SERVER_STARTED_RE = /IDE server has started,\s*listening on\s+https?:\/\/127\.0\.0\.1:(\d+)/i;
2387
+ function unwrapWechatCliExecutionError(result) {
2388
+ return result.kind === "retryable" ? result.error : result.value;
2389
+ }
2390
+ async function promptLoginRetry(errorLike, options, retryCount, operation) {
2391
+ const { nonInteractive, retryMode, retryTimeoutMs } = options;
2392
+ if (nonInteractive) {
2393
+ logger.error(i18nText("当前为非交互模式,检测到登录失效后直接失败。", "Non-interactive mode enabled, failing immediately on login expiration."));
2394
+ return "cancel";
2395
+ }
2396
+ if (!(retryMode === "always" || retryMode === "once" && retryCount < 1)) {
2397
+ await promptWechatIdeLoginRetry({
2398
+ allowRetry: false,
2399
+ error: errorLike,
2400
+ logger,
2401
+ promptOpenIdeLogin: true,
2402
+ retryTimeoutMs: operation.remainingMs(retryTimeoutMs),
2403
+ signal: operation.signal
2404
+ });
2405
+ logger.info(i18nText("当前重试策略不允许继续重试。", "Current retry policy does not allow further retries."));
2406
+ return "cancel";
2407
+ }
2408
+ return await promptWechatIdeLoginRetry({
2409
+ error: errorLike,
2410
+ logger,
2411
+ promptOpenIdeLogin: true,
2412
+ retryTimeoutMs: operation.remainingMs(retryTimeoutMs),
2413
+ signal: operation.signal
2414
+ });
2415
+ }
2416
+ function flushExecutionOutput(result) {
2417
+ if (!result || typeof result !== "object") return;
2418
+ const candidate = result;
2419
+ if (typeof candidate.stdout === "string" && candidate.stdout) process.stdout.write(candidate.stdout);
2420
+ if (typeof candidate.stderr === "string" && candidate.stderr) process.stderr.write(candidate.stderr);
2421
+ }
2422
+ function captureWechatDevtoolsServicePort(result, target) {
2423
+ if (!result || typeof result !== "object") return;
2424
+ const candidate = result;
2425
+ const match = [typeof candidate.stdout === "string" ? candidate.stdout : "", typeof candidate.stderr === "string" ? candidate.stderr : ""].join("\n").match(IDE_SERVER_STARTED_RE);
2426
+ if (!match?.[1]) return;
2427
+ setRuntimeWechatDevtoolsServicePort(Number.parseInt(match[1], 10), target);
2428
+ }
2429
+ /**
2430
+ * @description 运行微信开发者工具 CLI,并在登录失效时允许按键重试。
2431
+ */
2432
+ async function executeWechatCliWithRetry(cliPath, argv, options) {
2433
+ options.signal?.throwIfAborted();
2434
+ const target = await resolveWechatDevtoolsTarget({
2435
+ target: options.target,
2436
+ cliPath
2437
+ });
2438
+ const loginRetryOptions = resolveLoginRetryConfig(argv);
2439
+ const result = await runWithSuspendedSharedInput(async () => {
2440
+ return await runRetryableCommand({
2441
+ timeout: options.timeout,
2442
+ signal: options.signal,
2443
+ createCancelError: (result) => createWechatIdeLoginRequiredExitError(unwrapWechatCliExecutionError(result)),
2444
+ execute: async (operation) => {
2445
+ await assertWechatDevtoolsHost(target, {
2446
+ signal: operation.signal,
2447
+ timeout: operation.remainingMs()
2448
+ });
2449
+ operation.signal.throwIfAborted();
2450
+ try {
2451
+ return {
2452
+ kind: "result",
2453
+ value: await execute(target.cliPath, loginRetryOptions.runtimeArgv, {
2454
+ pipeStdout: false,
2455
+ pipeStderr: false,
2456
+ timeout: operation.remainingMs(),
2457
+ signal: operation.signal
2458
+ })
2459
+ };
2460
+ } catch (error) {
2461
+ if (!isWechatIdeLoginRequiredError(error)) throw error;
2462
+ return {
2463
+ error,
2464
+ kind: "retryable"
2465
+ };
2466
+ }
2467
+ },
2468
+ isRetryableResult: (result) => result.kind === "retryable" || isWechatIdeLoginRequiredError(result.value),
2469
+ onRetry: () => {
2470
+ logger.info(i18nText("正在重试连接微信开发者工具...", "Retrying to connect Wechat DevTools..."));
2471
+ },
2472
+ promptRetry: async (result, retryCount, operation) => {
2473
+ return await promptLoginRetry(unwrapWechatCliExecutionError(result), loginRetryOptions, retryCount, operation);
2474
+ },
2475
+ shouldRetry: (action) => action === "retry"
2476
+ });
2477
+ });
2478
+ if (result.kind === "retryable") throw createWechatIdeLoginRequiredExitError(result.error);
2479
+ if (isWechatIdeLoginRequiredError(result.value)) throw createWechatIdeLoginRequiredExitError(result.value);
2480
+ captureWechatDevtoolsServicePort(result.value, target);
2481
+ if (!options.silent) flushExecutionOutput(result.value);
2482
+ }
2483
+ /** 原生 CLI 可能启动或改变共享宿主,完整命令与有限重试共用机器租约。 */
2484
+ async function runWechatCliWithRetry(cliPath, argv, options = {}) {
2485
+ return await withMachineE2ELease(() => executeWechatCliWithRetry(cliPath, argv, options));
2486
+ }
2487
+ //#endregion
2488
+ //#region src/cli/run-wechat-cli.ts
2489
+ function shouldBootstrapWechatDevtools(command) {
2490
+ return command === "open" || command === "auto" || command === "auto-preview";
2491
+ }
2492
+ function appendOptionValue(argv, sourceArgv, optionName) {
2493
+ const value = readOptionValue(sourceArgv, optionName);
2494
+ if (value !== void 0) argv.push(optionName, value);
2495
+ }
2496
+ function createAutoPreviewWakeArgv(argv, trustProject) {
2497
+ if (argv[0] !== "auto-preview") return;
2498
+ const projectPath = readOptionValue(argv, "--project");
2499
+ const appid = readOptionValue(argv, "--appid");
2500
+ if (!projectPath && !appid) return;
2501
+ const wakeArgv = ["open"];
2502
+ appendOptionValue(wakeArgv, argv, "--project");
2503
+ appendOptionValue(wakeArgv, argv, "--appid");
2504
+ appendOptionValue(wakeArgv, argv, "--ext-appid");
2505
+ if (trustProject === true) wakeArgv.push("--trust-project");
2506
+ return wakeArgv;
2507
+ }
2508
+ function resolveBooleanCliOption(argv, optionName) {
2509
+ if (argv.includes(optionName)) return true;
2510
+ const rawValue = readOptionValue(argv, optionName);
2511
+ if (rawValue === void 0) return;
2512
+ const normalized = rawValue.trim().toLowerCase();
2513
+ if (normalized === "" || normalized === "true" || normalized === "1" || normalized === "on") return true;
2514
+ if (normalized === "false" || normalized === "0" || normalized === "off") return false;
2515
+ return true;
2516
+ }
2517
+ async function handleMissingCliPath(source) {
2518
+ if (source === "environment") throw new Error("WEAPP_IDE_CLI_PATH 指定的开发者工具不可用;已停止,不回退其他安装。");
2519
+ const message = source === "custom" ? i18nText("在当前自定义路径中未找到微信web开发者命令行工具,请重新指定路径。", "Cannot find Wechat Web DevTools CLI in custom path, please reconfigure it.") : i18nText(`未检测到微信web开发者命令行工具,请执行 ${colors.bold(colors.green("weapp-ide-cli config"))} 指定路径。`, `Wechat Web DevTools CLI not found, please run ${colors.bold(colors.green("weapp-ide-cli config"))} to configure it.`);
2520
+ logger.warn(message);
2521
+ await promptForCliPath();
2522
+ }
2523
+ async function maybeBootstrapWechatDevtoolsSettings(argv, target) {
2524
+ const command = argv[0];
2525
+ if (!shouldBootstrapWechatDevtools(command)) return;
2526
+ const config = await readCustomConfig();
2527
+ if (config.autoBootstrapDevtools === false) return;
2528
+ const projectPath = readOptionValue(argv, "--project");
2529
+ const trustProjectOption = resolveBooleanCliOption(argv, "--trust-project");
2530
+ const trustProject = trustProjectOption === void 0 ? config.autoTrustProject ?? false : trustProjectOption;
2531
+ await bootstrapWechatDevtoolsSettings({
2532
+ target,
2533
+ projectPath,
2534
+ trustProject
2535
+ });
2536
+ return { trustProject };
2537
+ }
2538
+ /** 在动作开始前选择安装,沿用原有平台检查和缺失路径配置提示。 */
2539
+ async function resolveWechatCliCommandTarget(options = {}) {
2540
+ if (!isOperatingSystemSupported(operatingSystemName)) {
2541
+ logger.warn(i18nText(`微信web开发者工具不支持当前平台:${operatingSystemName} !`, `Wechat Web DevTools CLI is not supported on current platform: ${operatingSystemName}!`));
2542
+ return;
2543
+ }
2544
+ if (options.cliPath || options.devtoolsChannel || options.devtoolsVersion || options.requireOfficialLatest) return await resolveWechatDevtoolsTarget(options);
2545
+ let target = options.target;
2546
+ if (!target) {
2547
+ const { cliPath, source } = await resolveCliPath();
2548
+ if (!cliPath) {
2549
+ await handleMissingCliPath(source);
2550
+ return;
2551
+ }
2552
+ target = await resolveWechatDevtoolsTarget({ cliPath });
2553
+ }
2554
+ return target;
2555
+ }
2556
+ /**
2557
+ * @description 执行微信开发者工具 CLI 阶段,包括环境检查、路径解析、bootstrap 与登录重试。
2558
+ */
2559
+ async function executeWechatCliCommand(argv, options) {
2560
+ const target = await resolveWechatCliCommandTarget(options);
2561
+ if (!target) return;
2562
+ await assertWechatDevtoolsHost(target, {
2563
+ signal: options.signal,
2564
+ timeout: options.timeout
2565
+ });
2566
+ const executionOptions = {
2567
+ ...options,
2568
+ target
2569
+ };
2570
+ const wakeArgv = createAutoPreviewWakeArgv(argv, (await maybeBootstrapWechatDevtoolsSettings(argv, target))?.trustProject);
2571
+ if (wakeArgv) await runWechatCliWithRetry(target.cliPath, wakeArgv, executionOptions);
2572
+ await runWechatCliWithRetry(target.cliPath, argv, executionOptions);
2573
+ }
2574
+ /** 配置预热与原生命令属于同一个宿主操作,排他租约在任何写入之前取得。 */
2575
+ async function runWechatCliCommand(argv, options = {}) {
2576
+ return await withMachineE2ELease(() => executeWechatCliCommand(argv, options));
2577
+ }
2578
+ //#endregion
1445
2579
  //#region src/cli/wechat-command-schema.ts
1446
2580
  const CACHE_CLEAN_TYPES = [
1447
2581
  "storage",
@@ -1526,6 +2660,321 @@ function validateWechatCliCommandArgs(argv) {
1526
2660
  }
1527
2661
  }
1528
2662
  //#endregion
2663
+ //#region src/cli/wechat-commands.ts
2664
+ function selectionOptions(options) {
2665
+ return Object.fromEntries([
2666
+ "cliPath",
2667
+ "target",
2668
+ "devtoolsChannel",
2669
+ "devtoolsVersion",
2670
+ "requireOfficialLatest"
2671
+ ].filter((key) => options[key] !== void 0).map((key) => [key, options[key]]));
2672
+ }
2673
+ async function runSelectedCommand(argv, options) {
2674
+ const selection = selectionOptions(options);
2675
+ if (Object.keys(selection).length) await runWechatCliCommand(argv, selection);
2676
+ else await runWechatCliCommand(argv);
2677
+ }
2678
+ const DEVTOOLS_SHUTDOWN_TIMEOUT_MS = 1e4;
2679
+ function appendProjectLocatorArgv(argv, options) {
2680
+ if (options.projectPath) argv.push("--project", path.resolve(options.projectPath));
2681
+ if (options.appid) argv.push("--appid", options.appid);
2682
+ if (options.extAppid) argv.push("--ext-appid", options.extAppid);
2683
+ }
2684
+ /**
2685
+ * @description 调用微信开发者工具 open 命令。
2686
+ */
2687
+ async function openSelectedWechatIde(options, target) {
2688
+ const argv = ["open"];
2689
+ appendProjectLocatorArgv(argv, options);
2690
+ if (options.platform) argv.push("--platform", options.platform);
2691
+ if (options.trustProject) argv.push("--trust-project");
2692
+ if (options.projectPath) try {
2693
+ await openWechatIdeProjectByHttp(options.projectPath, { target });
2694
+ return;
2695
+ } catch (error) {
2696
+ if (error instanceof Error && (error.message.startsWith("Runtime busy:") || "code" in error && (error.code === "WECHAT_DEVTOOLS_HOST_IDENTITY_MISMATCH" || error.code === "WECHAT_DEVTOOLS_INSTALLATION_SELECTION_CONFLICT"))) throw error;
2697
+ }
2698
+ await runWechatCliCommand(argv, { target });
2699
+ }
2700
+ /** 打开与兼容回退在同一机器租约内沿用固定安装,禁止重新选择宿主。 */
2701
+ async function openWechatIde(options = {}) {
2702
+ return await withMachineE2ELease(async () => {
2703
+ const target = await resolveWechatCliCommandTarget(options);
2704
+ if (target) {
2705
+ if (process.env["WEAPP_IDE_MANAGED_PROJECT_JOURNAL"]?.trim()) {
2706
+ if (!options.projectPath) throw new Error("Managed DevTools open requires an explicit project path; no project was opened.");
2707
+ await ensureManagedWechatProject(target, options.projectPath, { trustProject: options.trustProject });
2708
+ return;
2709
+ }
2710
+ return await openSelectedWechatIde(options, target);
2711
+ }
2712
+ });
2713
+ }
2714
+ /**
2715
+ * @description 调用微信开发者工具 login 命令。
2716
+ */
2717
+ async function loginWechatIde(options = {}) {
2718
+ const argv = ["login"];
2719
+ if (options.qrFormat) argv.push("--qr-format", options.qrFormat);
2720
+ if (options.qrOutput) argv.push("--qr-output", path.resolve(options.qrOutput));
2721
+ if (options.qrSize) argv.push("--qr-size", options.qrSize);
2722
+ if (options.resultOutput) argv.push("--result-output", path.resolve(options.resultOutput));
2723
+ await runSelectedCommand(argv, options);
2724
+ }
2725
+ /**
2726
+ * @description 调用微信开发者工具 islogin 命令。
2727
+ */
2728
+ async function isWechatIdeLoggedIn(options = {}) {
2729
+ const argv = ["islogin"];
2730
+ if (options.nonInteractive) argv.push("--non-interactive");
2731
+ if (options.silent) await runWechatCliCommand(argv, {
2732
+ ...selectionOptions(options),
2733
+ silent: true
2734
+ });
2735
+ else await runSelectedCommand(argv, options);
2736
+ }
2737
+ /**
2738
+ * @description 调用微信开发者工具 build-npm 命令。
2739
+ */
2740
+ async function buildWechatIdeNpm(options = {}) {
2741
+ const argv = [
2742
+ "build-npm",
2743
+ "--project",
2744
+ path.resolve(options.projectPath ?? process.cwd())
2745
+ ];
2746
+ if (options.compileType) argv.push("--compile-type", options.compileType);
2747
+ await runSelectedCommand(argv, options);
2748
+ }
2749
+ /**
2750
+ * @description 调用微信开发者工具 preview 命令。
2751
+ */
2752
+ async function previewWechatIde(options = {}) {
2753
+ const argv = ["preview"];
2754
+ appendProjectLocatorArgv(argv, options);
2755
+ if (options.qrFormat) argv.push("--qr-format", options.qrFormat);
2756
+ if (options.qrOutput) argv.push("--qr-output", path.resolve(options.qrOutput));
2757
+ if (options.qrSize) argv.push("--qr-size", options.qrSize);
2758
+ if (options.infoOutput) argv.push("--info-output", path.resolve(options.infoOutput));
2759
+ if (options.compileCondition) argv.push("--compile-condition", options.compileCondition);
2760
+ await runSelectedCommand(argv, options);
2761
+ }
2762
+ /**
2763
+ * @description 调用微信开发者工具 auto-preview 命令。
2764
+ */
2765
+ async function autoPreviewWechatIde(options = {}) {
2766
+ const argv = ["auto-preview"];
2767
+ appendProjectLocatorArgv(argv, options);
2768
+ if (options.infoOutput) argv.push("--info-output", path.resolve(options.infoOutput));
2769
+ if (options.compileCondition) argv.push("--compile-condition", options.compileCondition);
2770
+ await runSelectedCommand(argv, options);
2771
+ }
2772
+ /**
2773
+ * @description 调用微信开发者工具 auto 命令。
2774
+ */
2775
+ async function autoWechatIde(options = {}) {
2776
+ const argv = ["auto"];
2777
+ appendProjectLocatorArgv(argv, options);
2778
+ if (options.port) argv.push("--auto-port", options.port);
2779
+ if (options.account) argv.push("--auto-account", options.account);
2780
+ if (options.testTicket) argv.push("--test-ticket", options.testTicket);
2781
+ if (options.ticket) argv.push("--ticket", options.ticket);
2782
+ if (options.trustProject) argv.push("--trust-project");
2783
+ await runSelectedCommand(argv, options);
2784
+ }
2785
+ /**
2786
+ * @description 调用微信开发者工具 auto-replay 命令。
2787
+ */
2788
+ async function autoReplayWechatIde(options = {}) {
2789
+ const argv = ["auto-replay"];
2790
+ appendProjectLocatorArgv(argv, options);
2791
+ if (options.port) argv.push("--auto-port", options.port);
2792
+ if (options.account) argv.push("--auto-account", options.account);
2793
+ if (options.replayAll) argv.push("--replay-all");
2794
+ if (options.replayConfigPath) argv.push("--replay-config-path", path.resolve(options.replayConfigPath));
2795
+ if (options.testTicket) argv.push("--test-ticket", options.testTicket);
2796
+ if (options.ticket) argv.push("--ticket", options.ticket);
2797
+ if (options.trustProject) argv.push("--trust-project");
2798
+ await runSelectedCommand(argv, options);
2799
+ }
2800
+ /**
2801
+ * @description 调用微信开发者工具 upload 命令。
2802
+ */
2803
+ async function uploadWechatIde(options) {
2804
+ const argv = ["upload"];
2805
+ appendProjectLocatorArgv(argv, options);
2806
+ argv.push("--version", options.version, "--desc", options.desc);
2807
+ if (options.infoOutput) argv.push("--info-output", path.resolve(options.infoOutput));
2808
+ await runSelectedCommand(argv, options);
2809
+ }
2810
+ /**
2811
+ * @description 调用微信开发者工具 close 命令。
2812
+ */
2813
+ async function closeWechatIdeProject(options = {}) {
2814
+ if (options.projectPath !== void 0 && !options.projectPath.trim()) throw new Error("The explicit close projectPath must not be empty.");
2815
+ if (options.cliPath !== void 0 && !options.cliPath.trim()) throw new Error("The explicit close cliPath must not be empty.");
2816
+ const argv = ["close"];
2817
+ appendProjectLocatorArgv(argv, options);
2818
+ const target = options.target || options.cliPath || options.devtoolsChannel || options.devtoolsVersion ? await resolveWechatDevtoolsTarget(selectionOptions(options)) : void 0;
2819
+ await runWechatCliCommand(argv, {
2820
+ ...target ? { target } : selectionOptions(options),
2821
+ timeout: DEVTOOLS_SHUTDOWN_TIMEOUT_MS
2822
+ });
2823
+ }
2824
+ /**
2825
+ * @description 调用微信开发者工具 quit 命令。
2826
+ */
2827
+ async function quitWechatIde(options = {}) {
2828
+ await runWechatCliCommand(["quit"], {
2829
+ ...selectionOptions(options),
2830
+ timeout: DEVTOOLS_SHUTDOWN_TIMEOUT_MS
2831
+ });
2832
+ }
2833
+ /**
2834
+ * @description 调用微信开发者工具 cache 命令。
2835
+ */
2836
+ async function clearWechatIdeCache(options) {
2837
+ await runSelectedCommand([
2838
+ "cache",
2839
+ "--clean",
2840
+ options.clean
2841
+ ], options);
2842
+ }
2843
+ /**
2844
+ * @description 调用微信开发者工具 open-other 命令。
2845
+ */
2846
+ async function openWechatIdeOtherProject(options = {}) {
2847
+ await runSelectedCommand(["open-other"], options);
2848
+ }
2849
+ /**
2850
+ * @description 通过微信开发者工具 HTTP 服务端口重置指定项目的 fileutils 状态。
2851
+ */
2852
+ async function resetWechatIdeFileUtils(options) {
2853
+ const selection = selectionOptions(options);
2854
+ if (Object.keys(selection).length) await resetWechatIdeFileUtilsByHttp(path.resolve(options.projectPath), selection);
2855
+ else await resetWechatIdeFileUtilsByHttp(path.resolve(options.projectPath));
2856
+ }
2857
+ function createAutomatorSessionOptions(options) {
2858
+ return {
2859
+ cliPath: options.cliPath,
2860
+ target: options.target,
2861
+ ...selectionOptions(options),
2862
+ ...options.port ? { port: options.port } : {},
2863
+ preferOpenedSession: options.preferOpenedSession ?? true,
2864
+ preserveProjectRoot: options.preserveProjectRoot,
2865
+ projectPath: path.resolve(options.projectPath),
2866
+ ...options.sessionId ? { sessionId: options.sessionId } : {},
2867
+ sharedSession: options.sharedSession ?? true,
2868
+ timeout: options.timeout
2869
+ };
2870
+ }
2871
+ /**
2872
+ * @description 通过已打开或新建的 automator 会话获取开发者工具基础信息。
2873
+ */
2874
+ async function getWechatIdeToolInfo(options) {
2875
+ return await withMiniProgram$1(createAutomatorSessionOptions(options), async (miniProgram) => {
2876
+ return await miniProgram.toolInfo();
2877
+ });
2878
+ }
2879
+ /**
2880
+ * @description 通过已打开或新建的 automator 会话执行项目编译。
2881
+ */
2882
+ async function compileWechatIdeByAutomator(options) {
2883
+ return await withMiniProgram$1(createAutomatorSessionOptions(options), async (miniProgram) => {
2884
+ return await miniProgram.compile({ force: options.force });
2885
+ });
2886
+ }
2887
+ /**
2888
+ * @description 通过已打开或新建的 automator 会话清理开发者工具缓存。
2889
+ */
2890
+ async function clearWechatIdeCacheByAutomator(options) {
2891
+ return await withMiniProgram$1(createAutomatorSessionOptions(options), async (miniProgram) => {
2892
+ return await miniProgram.clearCache({ clean: options.clean });
2893
+ });
2894
+ }
2895
+ /**
2896
+ * @description 通过已打开或新建的 automator 会话获取当前 ticket。
2897
+ */
2898
+ async function getWechatIdeTicket(options) {
2899
+ return await withMiniProgram$1(createAutomatorSessionOptions(options), async (miniProgram) => {
2900
+ return await miniProgram.getTicket();
2901
+ });
2902
+ }
2903
+ /**
2904
+ * @description 通过已打开或新建的 automator 会话设置 ticket。
2905
+ */
2906
+ async function setWechatIdeTicket(options) {
2907
+ return await withMiniProgram$1(createAutomatorSessionOptions(options), async (miniProgram) => {
2908
+ await miniProgram.setTicket(options.ticket);
2909
+ });
2910
+ }
2911
+ /**
2912
+ * @description 通过已打开或新建的 automator 会话刷新 ticket。
2913
+ */
2914
+ async function refreshWechatIdeTicket(options) {
2915
+ return await withMiniProgram$1(createAutomatorSessionOptions(options), async (miniProgram) => {
2916
+ await miniProgram.refreshTicket();
2917
+ });
2918
+ }
2919
+ /**
2920
+ * @description 通过已打开或新建的 automator 会话获取测试账号列表。
2921
+ */
2922
+ async function getWechatIdeTestAccounts(options) {
2923
+ return await withMiniProgram$1(createAutomatorSessionOptions(options), async (miniProgram) => {
2924
+ return await miniProgram.testAccounts();
2925
+ });
2926
+ }
2927
+ /**
2928
+ * @description 调用微信开发者工具 build-apk 命令。
2929
+ */
2930
+ async function buildWechatIdeApk(options) {
2931
+ const argv = [
2932
+ "build-apk",
2933
+ "--key-store",
2934
+ path.resolve(options.keyStore),
2935
+ "--key-alias",
2936
+ options.keyAlias,
2937
+ "--key-pass",
2938
+ options.keyPass,
2939
+ "--store-pass",
2940
+ options.storePass,
2941
+ "--output",
2942
+ path.resolve(options.output)
2943
+ ];
2944
+ if (options.useAab !== void 0) argv.push("--use-aab", String(options.useAab));
2945
+ if (options.desc) argv.push("--desc", options.desc);
2946
+ if (options.isUploadResourceBundle) argv.push("--isUploadResourceBundle");
2947
+ if (options.resourceBundleVersion) argv.push("--resourceBundleVersion", options.resourceBundleVersion);
2948
+ if (options.resourceBundleDesc) argv.push("--resourceBundleDesc", options.resourceBundleDesc);
2949
+ await runSelectedCommand(argv, options);
2950
+ }
2951
+ /**
2952
+ * @description 调用微信开发者工具 build-ipa 命令。
2953
+ */
2954
+ async function buildWechatIdeIpa(options) {
2955
+ const argv = [
2956
+ "build-ipa",
2957
+ "--output",
2958
+ path.resolve(options.output),
2959
+ "--isDistribute",
2960
+ String(options.isDistribute)
2961
+ ];
2962
+ if (options.isRemoteBuild !== void 0) argv.push("--isRemoteBuild", String(options.isRemoteBuild));
2963
+ if (options.profilePath) argv.push("--profilePath", path.resolve(options.profilePath));
2964
+ if (options.certificateName) argv.push("--certificateName", options.certificateName);
2965
+ if (options.p12Path) argv.push("--p12Path", path.resolve(options.p12Path));
2966
+ if (options.p12Password) argv.push("--p12Password", options.p12Password);
2967
+ if (options.tpnsProfilePath) argv.push("--tpnsProfilePath", path.resolve(options.tpnsProfilePath));
2968
+ if (options.isUploadBeta !== void 0) argv.push("--isUploadBeta", String(options.isUploadBeta));
2969
+ if (options.isUploadResourceBundle) argv.push("--isUploadResourceBundle");
2970
+ if (options.resourceBundleVersion) argv.push("--resourceBundleVersion", options.resourceBundleVersion);
2971
+ if (options.resourceBundleDesc) argv.push("--resourceBundleDesc", options.resourceBundleDesc);
2972
+ if (options.versionName) argv.push("--versionName", options.versionName);
2973
+ if (options.versionCode !== void 0) argv.push("--versionCode", String(options.versionCode));
2974
+ if (options.versionDesc) argv.push("--versionDesc", options.versionDesc);
2975
+ await runSelectedCommand(argv, options);
2976
+ }
2977
+ //#endregion
1529
2978
  //#region src/cli/wechat-dispatch.ts
1530
2979
  function hasOption(argv, optionName) {
1531
2980
  return argv.includes(optionName) || argv.some((token) => token.startsWith(`${optionName}=`));
@@ -1676,7 +3125,15 @@ async function dispatchWechatCliCommand(argv) {
1676
3125
  return true;
1677
3126
  }
1678
3127
  if (command === "close") {
1679
- await closeWechatIdeProject();
3128
+ const projectPath = readOptionValue(argv, "--project", "-p");
3129
+ const cliPath = readOptionValue(argv, "--cli-path");
3130
+ if ((hasOption(argv, "--project") || hasOption(argv, "-p")) && (!projectPath || projectPath.startsWith("-"))) throw new Error("The close --project/-p option requires a value.");
3131
+ if (hasOption(argv, "--cli-path") && (!cliPath || cliPath.startsWith("-"))) throw new Error("The close --cli-path option requires a value.");
3132
+ if (projectPath !== void 0 || cliPath !== void 0) await closeWechatIdeProject({
3133
+ ...projectPath !== void 0 ? { projectPath } : {},
3134
+ ...cliPath !== void 0 ? { cliPath } : {}
3135
+ });
3136
+ else await closeWechatIdeProject();
1680
3137
  return true;
1681
3138
  }
1682
3139
  if (command === "quit") {
@@ -1731,6 +3188,11 @@ function createCli() {
1731
3188
  cli.command("help [command]", "显示命令帮助").allowUnknownOptions();
1732
3189
  for (const command of WECHAT_CLI_COMMAND_NAMES) cli.command(command, "微信开发者工具官方命令透传").allowUnknownOptions();
1733
3190
  for (const command of MINIDEV_NAMESPACE_COMMAND_NAMES) cli.command(`${command} [...args]`, "支付宝 minidev 命令透传").allowUnknownOptions();
3191
+ cli.command("devtools [...args]", "管理本机多版本开发者工具").allowUnknownOptions();
3192
+ cli.option("--devtools-channel <channel>", "选择 stable | rc | nightly");
3193
+ cli.option("--devtools-version <version>", "固定开发者工具产品版本");
3194
+ cli.option("--cli-path <path>", "固定开发者工具 CLI 安装路径");
3195
+ cli.option("--require-official-latest", "要求匹配所选渠道的官方最新版本");
1734
3196
  cli.command(`${CONFIG_COMMAND_NAME} [...args]`, "配置 weapp-ide-cli").allowUnknownOptions();
1735
3197
  cli.command(`mcp [...args]`, "启动 weapp-ide-cli MCP 服务").allowUnknownOptions();
1736
3198
  for (const command of AUTOMATOR_COMMAND_NAMES) cli.command(`${command} [...args]`, "automator 增强命令").allowUnknownOptions();
@@ -1779,7 +3241,7 @@ function createMinidevOpenArgv(argv) {
1779
3241
  /**
1780
3242
  * @description CLI 入口解析与分发。
1781
3243
  */
1782
- async function parse(argv) {
3244
+ async function parseSelected(argv) {
1783
3245
  validateLocaleOption(argv);
1784
3246
  configureLocaleFromArgv(argv, await getConfiguredLocale());
1785
3247
  const cli = createCli();
@@ -1801,12 +3263,16 @@ async function parse(argv) {
1801
3263
  await runMinidev(argv.slice(1));
1802
3264
  return;
1803
3265
  }
3266
+ if (matchedCommand === "devtools") {
3267
+ await handleDevtoolsCommand(argv.slice(1));
3268
+ return;
3269
+ }
1804
3270
  if (matchedCommand === "config") {
1805
3271
  await handleConfigCommand(argv.slice(1));
1806
3272
  return;
1807
3273
  }
1808
3274
  if (matchedCommand === "mcp") {
1809
- const { runMcpCommand } = await import("./run-mcp-C2SQ_hRJ.js");
3275
+ const { runMcpCommand } = await import("./run-mcp-83-VKZVA.js");
1810
3276
  await runMcpCommand(argv.slice(1));
1811
3277
  return;
1812
3278
  }
@@ -1823,5 +3289,17 @@ async function parse(argv) {
1823
3289
  if (await dispatchWechatCliCommand(formattedArgv)) return;
1824
3290
  await runWechatCliCommand(formattedArgv);
1825
3291
  }
3292
+ /** 在分发前剥离公共选择参数并固定本次安装。 */
3293
+ async function parse(argv) {
3294
+ if ([
3295
+ "help",
3296
+ "config",
3297
+ "devtools",
3298
+ "alipay",
3299
+ "ali",
3300
+ "minidev"
3301
+ ].includes(argv[0] ?? "") || argv.includes("--help") || argv.includes("-h")) return await parseSelected(argv);
3302
+ return await withWechatDevtoolsSelectionArgs(argv, parseSelected);
3303
+ }
1826
3304
  //#endregion
1827
- export { parseCompareArgs as C, printScreenshotHelp as S, AUTOMATOR_COMMAND_NAMES as _, runMinidev as a, runAutomatorCommand as b, runWechatIdeEngineBuild as c, CONFIG_COMMAND_NAME as d, MCP_COMMAND_NAME as f, isWeappIdeTopLevelCommand as g, WECHAT_CLI_COMMAND_NAMES as h, validateWechatCliCommandArgs as i, runWechatIdeEngineBuildByHttp as l, WEAPP_IDE_TOP_LEVEL_COMMAND_NAMES as m, parse as n, startForwardConsole as o, MINIDEV_NAMESPACE_COMMAND_NAMES as p, dispatchWechatCliCommand as r, isWechatIdeEngineBuildEndpointMissingError as s, createCli as t, handleConfigCommand as u, getAutomatorCommandHelp as v, printCompareHelp as w, parseScreenshotArgs as x, isAutomatorCommand as y };
3305
+ 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 };