weapp-ide-cli 6.1.9 → 6.2.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.
- package/README.md +18 -0
- package/dist/automator-BkZZsGtn.js +1 -0
- package/dist/automator-CRpB6_pA.js +576 -0
- package/dist/automator-session-gWEsSs_A.js +558 -0
- package/dist/{cli-2sY1sAVq.js → cli-ChMQqi5w.js} +1219 -52
- package/dist/cli.js +3 -3
- package/dist/commands-C1PF1B4P.js +528 -0
- package/dist/commands-CZVPfkE9.js +1 -0
- package/dist/devtoolsProjectOwnership-yDD6h0dO.js +1726 -0
- package/dist/index.d.ts +314 -54
- package/dist/index.js +8 -5
- package/dist/managed-Sq8y8FzP.js +212 -0
- package/dist/run-mcp-CPv8cesh.js +1 -0
- package/dist/{run-mcp-LqaWlQJ_.js → run-mcp-Dl_1TH1x.js} +18 -9
- package/dist/sessionStore-zdzy2UFM.js +605 -0
- package/package.json +12 -11
- package/dist/automator-session-fbanL2eW.js +0 -1362
- package/dist/commands-DpMG1w04.js +0 -2
- package/dist/commands-__RJ552L.js +0 -1359
- package/dist/run-mcp-C2SQ_hRJ.js +0 -2
|
@@ -1,15 +1,191 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import "./
|
|
4
|
-
import
|
|
1
|
+
import { D as logger, E as colors, T as operatingSystemName, _ as readCustomConfig, a as assertWechatDevtoolsPort, b as defaultCustomConfigFilePath, d as resolveDevtoolsAutomationDefaults, f as createAutoBootstrapDevtoolsConfig, g as overwriteCustomConfig, h as createLocaleConfig, i as assertWechatDevtoolsHost, l as getConfig, m as createCustomConfig, p as createAutoTrustProjectConfig, r as resolveWechatDevtoolsTarget, u as getConfiguredLocale, v as removeCustomConfigKey, w as isOperatingSystemSupported, x as resolvePath } from "./sessionStore-zdzy2UFM.js";
|
|
2
|
+
import { a as readManagedRecord, c as withManagedJournalLock, f as assertManagedInstallation, l as writeManagedRecord, m as sameManagedProcess, o as readManagedWechatProjectRecords, p as inspectManagedProjectHost, s as resolveManagedJournal } from "./devtoolsProjectOwnership-yDD6h0dO.js";
|
|
3
|
+
import { _ as parseAutomatorPort, a as bootstrapWechatDevtoolsSettings, g as resolveAutomatorSessionOptions, o as detectWechatDevtoolsServicePort } from "./automator-CRpB6_pA.js";
|
|
4
|
+
import { C as runWithSuspendedSharedInput, D as validateLocaleOption, E as i18nText, T as configureLocaleFromArgv, _ as isWechatIdeLoginRequiredError, b as promptWechatIdeLoginRetry, c as withMiniProgram$1, i as connectConsoleMiniProgram, l as runRetryableCommand, p as createWechatIdeLoginRequiredExitError, r as closeSharedMiniProgram, s as releaseSharedMiniProgram, t as acquireConsoleMiniProgram } from "./automator-session-gWEsSs_A.js";
|
|
5
|
+
import { a as navigateBack, c as pageStack, d as remote, f as scrollTo, g as tap, i as input, l as reLaunch, m as systemInfo, n as captureScreenshotBuffer, o as navigateTo, p as switchTab, r as currentPage, s as pageData, t as audit, u as redirectTo } from "./commands-C1PF1B4P.js";
|
|
6
|
+
import "./run-mcp-Dl_1TH1x.js";
|
|
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
|
|
14
|
+
import { OperationLifecycle, readWechatLoginState } from "@weapp-vite/miniprogram-automator/operation";
|
|
8
15
|
import { PNG } from "pngjs";
|
|
9
16
|
import pixelmatch from "pixelmatch";
|
|
10
|
-
import {
|
|
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-
|
|
497
|
+
const { takeScreenshot } = await import("./commands-CZVPfkE9.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
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
996
|
+
logger.warn(i18nText(`命令 ${command} 暂无帮助信息`, `No help available for command: ${command}`));
|
|
809
997
|
}
|
|
810
998
|
/**
|
|
811
999
|
* @description 执行 automator 子命令。
|
|
@@ -878,6 +1066,59 @@ function isWeappIdeTopLevelCommand(command) {
|
|
|
878
1066
|
return Boolean(command && WEAPP_IDE_TOP_LEVEL_COMMAND_SET.has(command));
|
|
879
1067
|
}
|
|
880
1068
|
//#endregion
|
|
1069
|
+
//#region src/cli/prompt.ts
|
|
1070
|
+
/**
|
|
1071
|
+
* @description 交互式提示并保存 CLI 路径
|
|
1072
|
+
*/
|
|
1073
|
+
async function promptForCliPath() {
|
|
1074
|
+
return await runWithSuspendedSharedInput(async () => {
|
|
1075
|
+
const rl = createInterface({
|
|
1076
|
+
input: stdin,
|
|
1077
|
+
output: stdout
|
|
1078
|
+
});
|
|
1079
|
+
try {
|
|
1080
|
+
logger.info(`请设置 ${colors.bold("微信web开发者工具 CLI")} 的路径`);
|
|
1081
|
+
logger.info("提示:命令行工具默认所在位置:");
|
|
1082
|
+
logger.info(`- MacOS: ${colors.green("<安装路径>/Contents/MacOS/cli")}`);
|
|
1083
|
+
logger.info(`- Windows: ${colors.green("<安装路径>/cli.bat")}`);
|
|
1084
|
+
logger.info(`- Linux: ${colors.green("<安装路径>/files/bin/bin/wechat-devtools-cli")}`);
|
|
1085
|
+
const cliPath = (await rl.question("请输入微信web开发者工具 CLI 路径:")).trim();
|
|
1086
|
+
if (!cliPath) {
|
|
1087
|
+
logger.error("路径不能为空,已取消本次配置。");
|
|
1088
|
+
return null;
|
|
1089
|
+
}
|
|
1090
|
+
try {
|
|
1091
|
+
const normalizedPath = await createCustomConfig({ cliPath });
|
|
1092
|
+
logger.info(`全局配置存储位置:${colors.green(defaultCustomConfigFilePath)}`);
|
|
1093
|
+
if (!await fs$1.pathExists(normalizedPath)) logger.warn("在当前路径未找到微信web开发者命令行工具,请确认路径是否正确。");
|
|
1094
|
+
return normalizedPath;
|
|
1095
|
+
} catch (error) {
|
|
1096
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
1097
|
+
logger.error(`保存配置失败:${reason}`);
|
|
1098
|
+
return null;
|
|
1099
|
+
}
|
|
1100
|
+
} finally {
|
|
1101
|
+
rl.close();
|
|
1102
|
+
}
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
//#endregion
|
|
1106
|
+
//#region src/cli/resolver.ts
|
|
1107
|
+
/**
|
|
1108
|
+
* @description 解析 CLI 路径并校验可用性
|
|
1109
|
+
*/
|
|
1110
|
+
async function resolveCliPath() {
|
|
1111
|
+
const config = await getConfig();
|
|
1112
|
+
if (!config.cliPath) return {
|
|
1113
|
+
cliPath: null,
|
|
1114
|
+
source: config.source
|
|
1115
|
+
};
|
|
1116
|
+
return {
|
|
1117
|
+
cliPath: await fs$1.pathExists(config.cliPath) ? config.cliPath : null,
|
|
1118
|
+
source: config.source
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
//#endregion
|
|
881
1122
|
//#region src/cli/config-command.ts
|
|
882
1123
|
function parseBooleanConfigValue(rawValue) {
|
|
883
1124
|
const normalized = rawValue.trim().toLowerCase();
|
|
@@ -898,13 +1139,13 @@ async function handleConfigCommand(argv) {
|
|
|
898
1139
|
const nextLocale = argv[1];
|
|
899
1140
|
if (nextLocale !== "zh" && nextLocale !== "en") throw new Error(i18nText("请使用 weapp config lang <zh|en> 切换语言", "Use weapp config lang <zh|en> to switch language"));
|
|
900
1141
|
await createLocaleConfig(nextLocale);
|
|
901
|
-
|
|
1142
|
+
logger.info(i18nText(`语言已切换为:${nextLocale === "zh" ? "中文" : "英文"}`, `Language switched to: ${nextLocale}`));
|
|
902
1143
|
return;
|
|
903
1144
|
}
|
|
904
1145
|
if (action === "show") {
|
|
905
1146
|
const config = await readCustomConfig();
|
|
906
1147
|
const effectiveDevtoolsDefaults = resolveDevtoolsAutomationDefaults(config);
|
|
907
|
-
|
|
1148
|
+
logger.info(i18nText(`配置文件路径:${colors.green(defaultCustomConfigFilePath)}`, `Config file: ${colors.green(defaultCustomConfigFilePath)}`));
|
|
908
1149
|
console.log(JSON.stringify({
|
|
909
1150
|
...config,
|
|
910
1151
|
effective: effectiveDevtoolsDefaults
|
|
@@ -932,25 +1173,25 @@ async function handleConfigCommand(argv) {
|
|
|
932
1173
|
if (!value) throw new Error(i18nText("请提供配置值,例如:weapp config set locale en", "Please provide a value, e.g. weapp config set locale en"));
|
|
933
1174
|
if (key === "cliPath") {
|
|
934
1175
|
await createCustomConfig({ cliPath: value });
|
|
935
|
-
|
|
1176
|
+
logger.info(i18nText("CLI 路径已更新。", "CLI path updated."));
|
|
936
1177
|
return;
|
|
937
1178
|
}
|
|
938
1179
|
if (key === "locale") {
|
|
939
1180
|
if (value !== "zh" && value !== "en") throw new Error(i18nText("locale 仅支持 zh 或 en", "locale only supports zh or en"));
|
|
940
1181
|
await createLocaleConfig(value);
|
|
941
|
-
|
|
1182
|
+
logger.info(i18nText(`语言已切换为:${value === "zh" ? "中文" : "英文"}`, `Language switched to: ${value}`));
|
|
942
1183
|
return;
|
|
943
1184
|
}
|
|
944
1185
|
if (key === "autoBootstrapDevtools") {
|
|
945
1186
|
const parsed = parseBooleanConfigValue(value);
|
|
946
1187
|
await createAutoBootstrapDevtoolsConfig(parsed);
|
|
947
|
-
|
|
1188
|
+
logger.info(i18nText(`autoBootstrapDevtools 已更新为:${parsed}`, `autoBootstrapDevtools updated to: ${parsed}`));
|
|
948
1189
|
return;
|
|
949
1190
|
}
|
|
950
1191
|
if (key === "autoTrustProject") {
|
|
951
1192
|
const parsed = parseBooleanConfigValue(value);
|
|
952
1193
|
await createAutoTrustProjectConfig(parsed);
|
|
953
|
-
|
|
1194
|
+
logger.info(i18nText(`autoTrustProject 已更新为:${parsed}`, `autoTrustProject updated to: ${parsed}`));
|
|
954
1195
|
return;
|
|
955
1196
|
}
|
|
956
1197
|
throw new Error(i18nText("仅支持设置配置项:cliPath | locale | autoBootstrapDevtools | autoTrustProject", "Supported keys for set: cliPath | locale | autoBootstrapDevtools | autoTrustProject"));
|
|
@@ -959,7 +1200,7 @@ async function handleConfigCommand(argv) {
|
|
|
959
1200
|
const key = argv[1];
|
|
960
1201
|
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
1202
|
await removeCustomConfigKey(key);
|
|
962
|
-
|
|
1203
|
+
logger.info(i18nText(`已清除配置项:${key}`, `Config key cleared: ${key}`));
|
|
963
1204
|
return;
|
|
964
1205
|
}
|
|
965
1206
|
if (action === "doctor") {
|
|
@@ -970,10 +1211,19 @@ async function handleConfigCommand(argv) {
|
|
|
970
1211
|
const hasValidCli = Boolean(resolvedCli.cliPath);
|
|
971
1212
|
const locale = rawConfig.locale ?? "zh";
|
|
972
1213
|
const effectiveDevtoolsDefaults = resolveDevtoolsAutomationDefaults(rawConfig);
|
|
1214
|
+
const installation = resolvedCli.cliPath ? await resolveWechatDevtoolsTarget({ cliPath: resolvedCli.cliPath }).then((target) => ({
|
|
1215
|
+
status: "identified",
|
|
1216
|
+
installationId: target.installationId,
|
|
1217
|
+
version: target.version ?? null,
|
|
1218
|
+
channel: target.channel ?? "unknown",
|
|
1219
|
+
profileDir: target.profileDir
|
|
1220
|
+
}), () => ({ status: "unavailable" })) : { status: "unavailable" };
|
|
973
1221
|
const report = {
|
|
974
1222
|
configFile: defaultCustomConfigFilePath,
|
|
975
1223
|
configFileExists: hasConfigFile,
|
|
976
1224
|
cliPath: rawConfig.cliPath ?? null,
|
|
1225
|
+
effectiveCliPath: resolvedCli.cliPath,
|
|
1226
|
+
installation,
|
|
977
1227
|
cliPathValid: hasValidCli,
|
|
978
1228
|
locale,
|
|
979
1229
|
autoBootstrapDevtools: rawConfig.autoBootstrapDevtools ?? null,
|
|
@@ -982,10 +1232,10 @@ async function handleConfigCommand(argv) {
|
|
|
982
1232
|
effectiveAutoTrustProject: effectiveDevtoolsDefaults.autoTrustProject,
|
|
983
1233
|
source: resolvedCli.source
|
|
984
1234
|
};
|
|
985
|
-
|
|
1235
|
+
logger.info(i18nText("配置诊断结果:", "Configuration diagnostics:"));
|
|
986
1236
|
console.log(JSON.stringify(report, null, 2));
|
|
987
|
-
if (!hasConfigFile)
|
|
988
|
-
if (hasCustomCli && !hasValidCli)
|
|
1237
|
+
if (!hasConfigFile) logger.warn(i18nText("未找到配置文件,可执行 weapp config 进行初始化。", "Config file not found. Run `weapp config` to initialize."));
|
|
1238
|
+
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
1239
|
return;
|
|
990
1240
|
}
|
|
991
1241
|
if (action === "export") {
|
|
@@ -996,7 +1246,7 @@ async function handleConfigCommand(argv) {
|
|
|
996
1246
|
spaces: 2,
|
|
997
1247
|
encoding: "utf8"
|
|
998
1248
|
});
|
|
999
|
-
|
|
1249
|
+
logger.info(i18nText(`配置已导出到:${colors.green(outputPath)}`, `Config exported to: ${colors.green(outputPath)}`));
|
|
1000
1250
|
return;
|
|
1001
1251
|
}
|
|
1002
1252
|
console.log(JSON.stringify(config, null, 2));
|
|
@@ -1018,12 +1268,221 @@ async function handleConfigCommand(argv) {
|
|
|
1018
1268
|
autoBootstrapDevtools,
|
|
1019
1269
|
autoTrustProject
|
|
1020
1270
|
});
|
|
1021
|
-
|
|
1271
|
+
logger.info(i18nText(`配置已从 ${colors.green(inputPath)} 导入`, `Config imported from ${colors.green(inputPath)}`));
|
|
1022
1272
|
return;
|
|
1023
1273
|
}
|
|
1024
1274
|
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
1275
|
}
|
|
1026
1276
|
//#endregion
|
|
1277
|
+
//#region src/cli/managedProjectGate.ts
|
|
1278
|
+
function sameTarget(first, second) {
|
|
1279
|
+
return first.installationId === second.installationId && first.cliPath === second.cliPath && first.appPath === second.appPath && first.profileDir === second.profileDir && first.version === second.version && first.channel === second.channel;
|
|
1280
|
+
}
|
|
1281
|
+
async function findConfirmedProject(journalPath, target, projectPath) {
|
|
1282
|
+
const matching = (await withManagedJournalLock(journalPath, async (scopeRoot) => {
|
|
1283
|
+
const scopeRecords = await readManagedWechatProjectRecords(scopeRoot);
|
|
1284
|
+
if (scopeRecords.some((record) => [
|
|
1285
|
+
"starting",
|
|
1286
|
+
"unconfirmed",
|
|
1287
|
+
"closing",
|
|
1288
|
+
"failed"
|
|
1289
|
+
].includes(record.state))) throw new Error("Managed DevTools journal contains unresolved ownership or cleanup; refusing a project mutation.");
|
|
1290
|
+
return scopeRoot === journalPath ? scopeRecords : await readManagedWechatProjectRecords(journalPath);
|
|
1291
|
+
})).filter((record) => record.state !== "released" && record.projectPath === projectPath && sameTarget(record.target, target));
|
|
1292
|
+
for (const record of matching) try {
|
|
1293
|
+
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.");
|
|
1294
|
+
const live = await inspectManagedProjectHost(target, record.port);
|
|
1295
|
+
if (!sameManagedProcess(live, record.host)) throw new Error("Managed DevTools project listener changed; refusing to reopen the project.");
|
|
1296
|
+
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.");
|
|
1297
|
+
} catch (error) {
|
|
1298
|
+
await withManagedJournalLock(record.journalPath, async () => {
|
|
1299
|
+
const current = await readManagedRecord(record.journalPath, record.id);
|
|
1300
|
+
if (current.state === "released") return;
|
|
1301
|
+
current.state = "failed";
|
|
1302
|
+
current.error = error instanceof Error ? error.message : String(error);
|
|
1303
|
+
await writeManagedRecord(current);
|
|
1304
|
+
});
|
|
1305
|
+
throw error;
|
|
1306
|
+
}
|
|
1307
|
+
return matching[0];
|
|
1308
|
+
}
|
|
1309
|
+
/** 开窗与构建入口先确认任务持有的项目回执,已登记项目保留原自动化端口。 */
|
|
1310
|
+
async function ensureManagedWechatProject(target, projectPath, options = {}) {
|
|
1311
|
+
options.signal?.throwIfAborted();
|
|
1312
|
+
const journalPath = resolveManagedJournal();
|
|
1313
|
+
if (!journalPath) return;
|
|
1314
|
+
if (!projectPath.trim()) throw new Error("Managed DevTools project mutation requires an explicit project path.");
|
|
1315
|
+
return await withMachineE2ELease(async () => {
|
|
1316
|
+
const resolvedProjectPath = path.resolve(projectPath);
|
|
1317
|
+
const existing = await findConfirmedProject(journalPath, target, resolvedProjectPath);
|
|
1318
|
+
options.signal?.throwIfAborted();
|
|
1319
|
+
await assertManagedInstallation(target);
|
|
1320
|
+
options.signal?.throwIfAborted();
|
|
1321
|
+
if (existing) return existing;
|
|
1322
|
+
const { launchAutomator } = await import("./automator-BkZZsGtn.js");
|
|
1323
|
+
(await launchAutomator({
|
|
1324
|
+
target,
|
|
1325
|
+
cliPath: target.cliPath,
|
|
1326
|
+
projectPath: resolvedProjectPath,
|
|
1327
|
+
runtimeProvider: "devtools",
|
|
1328
|
+
preserveProjectRoot: true,
|
|
1329
|
+
persistAsDefaultSession: true,
|
|
1330
|
+
signal: options.signal,
|
|
1331
|
+
trustProject: options.trustProject,
|
|
1332
|
+
timeout: 12e4
|
|
1333
|
+
})).disconnect();
|
|
1334
|
+
options.signal?.throwIfAborted();
|
|
1335
|
+
const confirmed = await findConfirmedProject(journalPath, target, resolvedProjectPath);
|
|
1336
|
+
if (!confirmed) throw new Error("Managed DevTools launcher returned without a confirmed task project; refusing a project mutation.");
|
|
1337
|
+
return confirmed;
|
|
1338
|
+
});
|
|
1339
|
+
}
|
|
1340
|
+
//#endregion
|
|
1341
|
+
//#region src/cli/wechatDevtoolsRuntimePort.ts
|
|
1342
|
+
const runtimePorts = /* @__PURE__ */ new Map();
|
|
1343
|
+
/**
|
|
1344
|
+
* @description 标记所选安装检测到的服务端口;无身份的旧调用仅保留在独立兼容槽中。
|
|
1345
|
+
*/
|
|
1346
|
+
function setRuntimeWechatDevtoolsServicePort(port, target) {
|
|
1347
|
+
const key = target?.installationId ?? "";
|
|
1348
|
+
if (typeof port !== "number" || !Number.isInteger(port) || port <= 0 || port > 65535) {
|
|
1349
|
+
runtimePorts.delete(key);
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
runtimePorts.set(key, port);
|
|
1353
|
+
}
|
|
1354
|
+
/**
|
|
1355
|
+
* @description 只读取所选安装的服务端口,不借用其他安装或无身份的旧记录。
|
|
1356
|
+
*/
|
|
1357
|
+
function getRuntimeWechatDevtoolsServicePort(target) {
|
|
1358
|
+
return runtimePorts.get(target?.installationId ?? "");
|
|
1359
|
+
}
|
|
1360
|
+
//#endregion
|
|
1361
|
+
//#region src/cli/http.ts
|
|
1362
|
+
const ENGINE_BUILD_NOT_START = "NOT_START";
|
|
1363
|
+
const ENGINE_BUILD_OPEN_PROJECT = "OPEN_PROJECT";
|
|
1364
|
+
const ENGINE_BUILD_BUILDING = "BUILDING";
|
|
1365
|
+
const ENGINE_BUILD_END = "END";
|
|
1366
|
+
const ENGINE_BUILD_ERROR = "ERROR";
|
|
1367
|
+
function createWechatDevtoolsHttpError(message, code) {
|
|
1368
|
+
const error = new Error(message);
|
|
1369
|
+
error.code = code;
|
|
1370
|
+
return error;
|
|
1371
|
+
}
|
|
1372
|
+
async function resolveWechatDevtoolsHttpPort(target, port) {
|
|
1373
|
+
if (port !== void 0) {
|
|
1374
|
+
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");
|
|
1375
|
+
return port;
|
|
1376
|
+
}
|
|
1377
|
+
const runtimePort = getRuntimeWechatDevtoolsServicePort(target);
|
|
1378
|
+
if (runtimePort) return runtimePort;
|
|
1379
|
+
const detected = await detectWechatDevtoolsServicePort({ target });
|
|
1380
|
+
if (detected.servicePortEnabled === false) throw createWechatDevtoolsHttpError("WECHAT_DEVTOOLS_SERVICE_PORT_DISABLED", "WECHAT_DEVTOOLS_SERVICE_PORT_DISABLED");
|
|
1381
|
+
if (!detected.servicePort) throw createWechatDevtoolsHttpError("The selected DevTools installation has no known HTTP service port.", "WECHAT_DEVTOOLS_SERVICE_PORT_UNKNOWN");
|
|
1382
|
+
return detected.servicePort;
|
|
1383
|
+
}
|
|
1384
|
+
function createWechatDevtoolsHttpUrl(port, pathname, query) {
|
|
1385
|
+
const url = new URL(`http://127.0.0.1:${port}${pathname}`);
|
|
1386
|
+
for (const [key, value] of Object.entries(query)) url.searchParams.set(key, value);
|
|
1387
|
+
return url;
|
|
1388
|
+
}
|
|
1389
|
+
function parseWechatDevtoolsEngineBuildResult(body) {
|
|
1390
|
+
try {
|
|
1391
|
+
return JSON.parse(body);
|
|
1392
|
+
} catch {
|
|
1393
|
+
return {};
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
async function requestSelectedWechatDevtoolsHttp(pathname, query, options = {}) {
|
|
1397
|
+
options.signal?.throwIfAborted();
|
|
1398
|
+
const target = await resolveWechatDevtoolsTarget(options);
|
|
1399
|
+
options.signal?.throwIfAborted();
|
|
1400
|
+
const projectParameter = pathname === "/v2/open" ? "project" : pathname === "/engine/build" ? "projectpath" : void 0;
|
|
1401
|
+
if (projectParameter) {
|
|
1402
|
+
await ensureManagedWechatProject(target, query[projectParameter] ?? "", { signal: options.signal });
|
|
1403
|
+
options.signal?.throwIfAborted();
|
|
1404
|
+
}
|
|
1405
|
+
const port = await resolveWechatDevtoolsHttpPort(target, options.port).catch((error) => {
|
|
1406
|
+
options.signal?.throwIfAborted();
|
|
1407
|
+
throw error;
|
|
1408
|
+
});
|
|
1409
|
+
options.signal?.throwIfAborted();
|
|
1410
|
+
await assertWechatDevtoolsPort(target, port, {
|
|
1411
|
+
signal: options.signal,
|
|
1412
|
+
timeout: options.timeoutMs
|
|
1413
|
+
});
|
|
1414
|
+
options.signal?.throwIfAborted();
|
|
1415
|
+
const url = createWechatDevtoolsHttpUrl(port, pathname, query);
|
|
1416
|
+
const controller = new AbortController();
|
|
1417
|
+
const signal = options.signal ? AbortSignal.any([options.signal, controller.signal]) : controller.signal;
|
|
1418
|
+
const timeout = setTimeout(() => {
|
|
1419
|
+
controller.abort(createWechatDevtoolsHttpError("WECHAT_DEVTOOLS_HTTP_TIMEOUT", "WECHAT_DEVTOOLS_HTTP_TIMEOUT"));
|
|
1420
|
+
}, options.timeoutMs ?? 1e4);
|
|
1421
|
+
try {
|
|
1422
|
+
const response = await fetch(url, {
|
|
1423
|
+
method: "GET",
|
|
1424
|
+
signal
|
|
1425
|
+
});
|
|
1426
|
+
const body = await response.text();
|
|
1427
|
+
signal.throwIfAborted();
|
|
1428
|
+
if (!response.ok) throw createWechatDevtoolsHttpError(body || `HTTP ${response.status}`, "WECHAT_DEVTOOLS_HTTP_REQUEST_FAILED");
|
|
1429
|
+
return body;
|
|
1430
|
+
} catch (error) {
|
|
1431
|
+
signal.throwIfAborted();
|
|
1432
|
+
throw error;
|
|
1433
|
+
} finally {
|
|
1434
|
+
clearTimeout(timeout);
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
/** 请求期间持有机器租约,避免 E2E 与另一入口同时更改共享宿主。 */
|
|
1438
|
+
async function requestWechatDevtoolsHttp(pathname, query, options = {}) {
|
|
1439
|
+
options.signal?.throwIfAborted();
|
|
1440
|
+
return await withMachineE2ELease(() => requestSelectedWechatDevtoolsHttp(pathname, query, options));
|
|
1441
|
+
}
|
|
1442
|
+
/**
|
|
1443
|
+
* @description 请求微信开发者工具打开项目并返回原始响应;目标 runtime 状态仍需通过会话或界面确认。
|
|
1444
|
+
*/
|
|
1445
|
+
async function openWechatIdeProjectByHttp(projectPath, options = {}) {
|
|
1446
|
+
const body = await requestWechatDevtoolsHttp("/v2/open", { project: path.resolve(projectPath) }, options);
|
|
1447
|
+
const response = body.trim();
|
|
1448
|
+
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");
|
|
1449
|
+
return body;
|
|
1450
|
+
}
|
|
1451
|
+
/**
|
|
1452
|
+
* @description 通过微信开发者工具 HTTP 服务端口重置当前项目的 fileutils 状态。
|
|
1453
|
+
*/
|
|
1454
|
+
async function resetWechatIdeFileUtilsByHttp(projectPath, options = {}) {
|
|
1455
|
+
return await requestWechatDevtoolsHttp("/v2/resetfileutils", { project: path.resolve(projectPath) }, options);
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* @description 通过微信开发者工具 HTTP 服务端口触发 engine build。
|
|
1459
|
+
*/
|
|
1460
|
+
async function startWechatIdeEngineBuildByHttp(projectPath, options = {}) {
|
|
1461
|
+
return { body: await requestWechatDevtoolsHttp("/engine/build", { projectpath: path.resolve(projectPath) }, options) };
|
|
1462
|
+
}
|
|
1463
|
+
/**
|
|
1464
|
+
* @description 轮询微信开发者工具 engine build 状态。
|
|
1465
|
+
*/
|
|
1466
|
+
async function pollWechatIdeEngineBuildResultByHttp(options = {}) {
|
|
1467
|
+
const body = await requestWechatDevtoolsHttp("/engine/buildResult/", {}, options);
|
|
1468
|
+
const parsed = parseWechatDevtoolsEngineBuildResult(body);
|
|
1469
|
+
const status = parsed.status;
|
|
1470
|
+
return {
|
|
1471
|
+
body,
|
|
1472
|
+
done: status === ENGINE_BUILD_END,
|
|
1473
|
+
failed: status === ENGINE_BUILD_ERROR,
|
|
1474
|
+
msg: parsed.msg,
|
|
1475
|
+
status
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
const WECHAT_DEVTOOLS_ENGINE_BUILD_STATUSES = {
|
|
1479
|
+
BUILDING: ENGINE_BUILD_BUILDING,
|
|
1480
|
+
END: ENGINE_BUILD_END,
|
|
1481
|
+
ERROR: ENGINE_BUILD_ERROR,
|
|
1482
|
+
NOT_START: ENGINE_BUILD_NOT_START,
|
|
1483
|
+
OPEN_PROJECT: ENGINE_BUILD_OPEN_PROJECT
|
|
1484
|
+
};
|
|
1485
|
+
//#endregion
|
|
1027
1486
|
//#region src/cli/engine.ts
|
|
1028
1487
|
function createEngineBuildError(message, code) {
|
|
1029
1488
|
const error = new Error(message);
|
|
@@ -1089,13 +1548,16 @@ function compactOutput(value) {
|
|
|
1089
1548
|
}
|
|
1090
1549
|
async function runWechatIdeEngineBuildByCli(projectPath, options = {}) {
|
|
1091
1550
|
options.signal?.throwIfAborted();
|
|
1092
|
-
const
|
|
1551
|
+
const target = await resolveWechatDevtoolsTarget(options).catch((error) => {
|
|
1093
1552
|
options.signal?.throwIfAborted();
|
|
1094
1553
|
throw error;
|
|
1095
1554
|
});
|
|
1096
1555
|
options.signal?.throwIfAborted();
|
|
1097
|
-
|
|
1098
|
-
|
|
1556
|
+
await ensureManagedWechatProject(target, projectPath, { signal: options.signal });
|
|
1557
|
+
options.signal?.throwIfAborted();
|
|
1558
|
+
await assertWechatDevtoolsHost(target, { signal: options.signal });
|
|
1559
|
+
options.signal?.throwIfAborted();
|
|
1560
|
+
const result = await execa(target.cliPath, [
|
|
1099
1561
|
"engine",
|
|
1100
1562
|
"build",
|
|
1101
1563
|
path.resolve(projectPath)
|
|
@@ -1115,20 +1577,20 @@ async function runWechatIdeEngineBuildByCli(projectPath, options = {}) {
|
|
|
1115
1577
|
await writeEngineBuildLog(options.logPath, output);
|
|
1116
1578
|
options.signal?.throwIfAborted();
|
|
1117
1579
|
if ((result.exitCode ?? 1) === 0) {
|
|
1118
|
-
if (stdout) process.stdout.write(stdout);
|
|
1119
|
-
if (stderr) process.stderr.write(stderr);
|
|
1580
|
+
if (stdout && !options.quiet) process.stdout.write(stdout);
|
|
1581
|
+
if (stderr && !options.quiet) process.stderr.write(stderr);
|
|
1120
1582
|
return;
|
|
1121
1583
|
}
|
|
1122
1584
|
if (ENGINE_BUILD_CLI_OPENED_PATTERN.test(output)) return;
|
|
1123
1585
|
if (isEngineBuildEndpointMissingError(output)) throw createEngineBuildEndpointMissingError();
|
|
1124
|
-
if (stdout) process.stdout.write(stdout);
|
|
1125
|
-
if (stderr) process.stderr.write(stderr);
|
|
1586
|
+
if (stdout && !options.quiet) process.stdout.write(stdout);
|
|
1587
|
+
if (stderr && !options.quiet) process.stderr.write(stderr);
|
|
1126
1588
|
throw createEngineBuildError(compactOutput(stderr) || compactOutput(stdout) || `WECHAT_DEVTOOLS_ENGINE_BUILD_CLI_FAILED:${result.exitCode ?? 1}`, "WECHAT_DEVTOOLS_ENGINE_BUILD_CLI_FAILED");
|
|
1127
1589
|
}
|
|
1128
1590
|
/**
|
|
1129
1591
|
* @description 通过开发者工具 HTTP 服务端口执行 engine build,并轮询直到构建结束。
|
|
1130
1592
|
*/
|
|
1131
|
-
async function
|
|
1593
|
+
async function runSelectedWechatIdeEngineBuildByHttp(projectPath, options = {}) {
|
|
1132
1594
|
options.signal?.throwIfAborted();
|
|
1133
1595
|
await startWechatIdeEngineBuildByHttp(projectPath, options);
|
|
1134
1596
|
const startedAt = Date.now();
|
|
@@ -1147,11 +1609,11 @@ async function runWechatIdeEngineBuildByHttp(projectPath, options = {}) {
|
|
|
1147
1609
|
/**
|
|
1148
1610
|
* @description 以更接近官方 CLI 的方式执行 engine build,并支持将构建日志写入文件。
|
|
1149
1611
|
*/
|
|
1150
|
-
async function
|
|
1612
|
+
async function runSelectedWechatIdeEngineBuild(projectPath, options = {}) {
|
|
1151
1613
|
const logs = [];
|
|
1152
1614
|
let lastLoggedMessage;
|
|
1153
1615
|
try {
|
|
1154
|
-
const result = await
|
|
1616
|
+
const result = await runSelectedWechatIdeEngineBuildByHttp(projectPath, {
|
|
1155
1617
|
...options,
|
|
1156
1618
|
onProgress: (progress) => {
|
|
1157
1619
|
if (progress.msg && progress.msg !== lastLoggedMessage) {
|
|
@@ -1175,6 +1637,67 @@ async function runWechatIdeEngineBuild(projectPath, options = {}) {
|
|
|
1175
1637
|
throw error;
|
|
1176
1638
|
}
|
|
1177
1639
|
}
|
|
1640
|
+
/** 固定安装上下文后完整持有 HTTP 构建与轮询期间的租约。 */
|
|
1641
|
+
async function runWechatIdeEngineBuildByHttp(projectPath, options = {}) {
|
|
1642
|
+
options.signal?.throwIfAborted();
|
|
1643
|
+
return await withMachineE2ELease(async () => {
|
|
1644
|
+
const target = await resolveWechatDevtoolsTarget(options);
|
|
1645
|
+
options.signal?.throwIfAborted();
|
|
1646
|
+
return await runSelectedWechatIdeEngineBuildByHttp(projectPath, {
|
|
1647
|
+
...options,
|
|
1648
|
+
target
|
|
1649
|
+
});
|
|
1650
|
+
});
|
|
1651
|
+
}
|
|
1652
|
+
/** HTTP、轮询与 CLI 回退沿用同一次解析的安装。 */
|
|
1653
|
+
async function runWechatIdeEngineBuild(projectPath, options = {}) {
|
|
1654
|
+
options.signal?.throwIfAborted();
|
|
1655
|
+
return await withMachineE2ELease(async () => {
|
|
1656
|
+
const target = await resolveWechatDevtoolsTarget(options);
|
|
1657
|
+
options.signal?.throwIfAborted();
|
|
1658
|
+
return await runSelectedWechatIdeEngineBuild(projectPath, {
|
|
1659
|
+
...options,
|
|
1660
|
+
target
|
|
1661
|
+
});
|
|
1662
|
+
});
|
|
1663
|
+
}
|
|
1664
|
+
async function prepareSelectedAcceptanceProject(projectPath, signal, options) {
|
|
1665
|
+
await openWechatIdeProjectByHttp(projectPath, {
|
|
1666
|
+
...options,
|
|
1667
|
+
signal,
|
|
1668
|
+
timeoutMs: 1e4
|
|
1669
|
+
});
|
|
1670
|
+
await sleep$1(1e3, signal);
|
|
1671
|
+
await resetWechatIdeFileUtilsByHttp(projectPath, {
|
|
1672
|
+
...options,
|
|
1673
|
+
signal,
|
|
1674
|
+
timeoutMs: 1e4
|
|
1675
|
+
});
|
|
1676
|
+
const result = await runWechatIdeEngineBuild(projectPath, {
|
|
1677
|
+
...options,
|
|
1678
|
+
signal,
|
|
1679
|
+
overallTimeoutMs: 6e4,
|
|
1680
|
+
timeoutMs: 1e4,
|
|
1681
|
+
quiet: true
|
|
1682
|
+
});
|
|
1683
|
+
if (!result) await sleep$1(1500, signal);
|
|
1684
|
+
return result;
|
|
1685
|
+
}
|
|
1686
|
+
/** 在验收交互前等待 IDE 消费产物,并沿用该会话选择的安装。 */
|
|
1687
|
+
async function prepareAcceptanceProject(projectPath, signal, options = {}) {
|
|
1688
|
+
signal.throwIfAborted();
|
|
1689
|
+
if (options.runtimeProvider === "headless") return;
|
|
1690
|
+
return await withMachineE2ELease(async () => {
|
|
1691
|
+
const target = await resolveWechatDevtoolsTarget(options);
|
|
1692
|
+
signal.throwIfAborted();
|
|
1693
|
+
await ensureManagedWechatProject(target, projectPath, { signal });
|
|
1694
|
+
signal.throwIfAborted();
|
|
1695
|
+
return await prepareSelectedAcceptanceProject(projectPath, signal, {
|
|
1696
|
+
...options,
|
|
1697
|
+
target
|
|
1698
|
+
});
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1178
1701
|
//#endregion
|
|
1179
1702
|
//#region src/cli/forwardConsole.ts
|
|
1180
1703
|
const DEFAULT_FORWARD_CONSOLE_LEVELS = [
|
|
@@ -1225,15 +1748,15 @@ function printForwardConsoleEvent(event) {
|
|
|
1225
1748
|
const line = `${formatForwardConsolePrefix(event.level)} ${formatForwardConsoleMessage(event)}`;
|
|
1226
1749
|
switch (event.level) {
|
|
1227
1750
|
case "error":
|
|
1228
|
-
|
|
1751
|
+
logger.error(line);
|
|
1229
1752
|
return;
|
|
1230
1753
|
case "warn":
|
|
1231
|
-
|
|
1754
|
+
logger.warn(line);
|
|
1232
1755
|
return;
|
|
1233
1756
|
case "info":
|
|
1234
|
-
|
|
1757
|
+
logger.info(line);
|
|
1235
1758
|
return;
|
|
1236
|
-
default:
|
|
1759
|
+
default: logger.log(line);
|
|
1237
1760
|
}
|
|
1238
1761
|
}
|
|
1239
1762
|
function formatConsoleArgument(value) {
|
|
@@ -1319,8 +1842,8 @@ async function acquireForwardConsoleMiniProgram(options) {
|
|
|
1319
1842
|
return miniProgram;
|
|
1320
1843
|
} catch (error) {
|
|
1321
1844
|
lastError = error;
|
|
1322
|
-
if (miniProgram) releaseSharedMiniProgram(options.projectPath, options.sessionId || options.port);
|
|
1323
|
-
await closeSharedMiniProgram(options.projectPath, options.sessionId || options.port);
|
|
1845
|
+
if (miniProgram) releaseSharedMiniProgram(options.projectPath, options.sessionId || options.port, options);
|
|
1846
|
+
await closeSharedMiniProgram(options.projectPath, options.sessionId || options.port, options);
|
|
1324
1847
|
if (attempt < ENABLE_LOG_RETRY_TIMES) await sleep(ENABLE_LOG_RETRY_DELAY_MS);
|
|
1325
1848
|
}
|
|
1326
1849
|
}
|
|
@@ -1351,6 +1874,7 @@ function startEnableLogRefresh(miniProgram, options, refreshOpenedSession) {
|
|
|
1351
1874
|
* @description 启动小程序控制台日志转发,并保持 automator 会话常驻。
|
|
1352
1875
|
*/
|
|
1353
1876
|
async function startForwardConsole(options) {
|
|
1877
|
+
options = options.miniProgram ? options : await resolveAutomatorSessionOptions(options);
|
|
1354
1878
|
const usesSharedSession = !options.miniProgram;
|
|
1355
1879
|
const miniProgram = await acquireForwardConsoleMiniProgram(options);
|
|
1356
1880
|
const logLevels = new Set(options.logLevels?.length ? options.logLevels : DEFAULT_FORWARD_CONSOLE_LEVELS);
|
|
@@ -1393,7 +1917,7 @@ async function startForwardConsole(options) {
|
|
|
1393
1917
|
auxiliaryMiniProgram.disconnect();
|
|
1394
1918
|
auxiliaryMiniProgram = void 0;
|
|
1395
1919
|
}
|
|
1396
|
-
if (usesSharedSession) releaseSharedMiniProgram(options.projectPath, options.sessionId || options.port);
|
|
1920
|
+
if (usesSharedSession) releaseSharedMiniProgram(options.projectPath, options.sessionId || options.port, options);
|
|
1397
1921
|
};
|
|
1398
1922
|
miniProgram.on("console", onConsole);
|
|
1399
1923
|
miniProgram.on("exception", onException);
|
|
@@ -1418,6 +1942,117 @@ async function startForwardConsole(options) {
|
|
|
1418
1942
|
return { close: closeSession };
|
|
1419
1943
|
}
|
|
1420
1944
|
//#endregion
|
|
1945
|
+
//#region src/utils/argv.ts
|
|
1946
|
+
function ensurePathArgument(argv, optionIndex) {
|
|
1947
|
+
const paramIdx = optionIndex + 1;
|
|
1948
|
+
const param = argv[paramIdx];
|
|
1949
|
+
if (param && !param.startsWith("-")) argv[paramIdx] = resolvePath(param);
|
|
1950
|
+
else argv.splice(paramIdx, 0, process.cwd());
|
|
1951
|
+
return argv;
|
|
1952
|
+
}
|
|
1953
|
+
/**
|
|
1954
|
+
* @description 依次应用 argv 处理函数(不修改原始 argv)
|
|
1955
|
+
*/
|
|
1956
|
+
function transformArgv(argv, transforms) {
|
|
1957
|
+
return transforms.reduce((current, transform) => transform(current), [...argv]);
|
|
1958
|
+
}
|
|
1959
|
+
/**
|
|
1960
|
+
* @description 创建参数别名转换器
|
|
1961
|
+
*/
|
|
1962
|
+
function createAlias(entry) {
|
|
1963
|
+
return (input) => {
|
|
1964
|
+
const argv = [...input];
|
|
1965
|
+
let optionIndex = argv.indexOf(entry.find);
|
|
1966
|
+
if (optionIndex > -1) argv.splice(optionIndex, 1, entry.replacement);
|
|
1967
|
+
else optionIndex = argv.indexOf(entry.replacement);
|
|
1968
|
+
if (optionIndex === -1) return argv;
|
|
1969
|
+
return ensurePathArgument(argv, optionIndex);
|
|
1970
|
+
};
|
|
1971
|
+
}
|
|
1972
|
+
/**
|
|
1973
|
+
* @description 创建路径参数兼容转换器(补全或规范化路径)
|
|
1974
|
+
*/
|
|
1975
|
+
function createPathCompat(option) {
|
|
1976
|
+
return (input) => {
|
|
1977
|
+
const argv = [...input];
|
|
1978
|
+
const optionIndex = argv.indexOf(option);
|
|
1979
|
+
if (optionIndex === -1) return argv;
|
|
1980
|
+
return ensurePathArgument(argv, optionIndex);
|
|
1981
|
+
};
|
|
1982
|
+
}
|
|
1983
|
+
//#endregion
|
|
1984
|
+
//#region src/utils/exec.ts
|
|
1985
|
+
/**
|
|
1986
|
+
* @description 执行 CLI 命令并透传输出
|
|
1987
|
+
*/
|
|
1988
|
+
async function execute(cliPath, argv, options = {}) {
|
|
1989
|
+
const { pipeStdout = true, pipeStderr = true, timeout } = options;
|
|
1990
|
+
const { execa } = await import("execa");
|
|
1991
|
+
const task = execa(cliPath, argv, {
|
|
1992
|
+
timeout,
|
|
1993
|
+
cancelSignal: options.signal,
|
|
1994
|
+
forceKillAfterDelay: 100
|
|
1995
|
+
});
|
|
1996
|
+
if (pipeStdout) task?.stdout?.pipe(process.stdout);
|
|
1997
|
+
if (pipeStderr) task?.stderr?.pipe(process.stderr);
|
|
1998
|
+
return await task;
|
|
1999
|
+
}
|
|
2000
|
+
//#endregion
|
|
2001
|
+
//#region src/cli/loginQuery.ts
|
|
2002
|
+
/** 读取原生 CLI 的明确登录结果;日志、空输出与相互矛盾的响应均不能证明登录状态。 */
|
|
2003
|
+
function parseLoginOutput(stdout) {
|
|
2004
|
+
const login = readWechatLoginState(stdout);
|
|
2005
|
+
if (login !== void 0) return {
|
|
2006
|
+
status: "success",
|
|
2007
|
+
login
|
|
2008
|
+
};
|
|
2009
|
+
return {
|
|
2010
|
+
status: "unknown",
|
|
2011
|
+
reason: "invalid-response"
|
|
2012
|
+
};
|
|
2013
|
+
}
|
|
2014
|
+
/** 显式调用指定 CLI 查询登录语义,不重试、不提示登录;原生 CLI 可能启动 IDE。 */
|
|
2015
|
+
async function queryWechatIdeLogin(cliPath, options = {}) {
|
|
2016
|
+
const timeout = options.timeout ?? 3e3;
|
|
2017
|
+
if (!Number.isFinite(timeout) || timeout <= 0) throw new TypeError("登录查询 timeout 必须是有限正数。");
|
|
2018
|
+
const operation = new OperationLifecycle(timeout, "IDE login query", options.signal);
|
|
2019
|
+
try {
|
|
2020
|
+
return await operation.run((scope) => withMachineE2ELease(async () => {
|
|
2021
|
+
const target = await scope.step(() => resolveWechatDevtoolsTarget({ cliPath }), { stage: "installation" });
|
|
2022
|
+
await scope.step(() => assertWechatDevtoolsHost(target, {
|
|
2023
|
+
signal: scope.signal,
|
|
2024
|
+
timeout: scope.remainingMs()
|
|
2025
|
+
}), {
|
|
2026
|
+
stage: "host-identity",
|
|
2027
|
+
waitForExit: true
|
|
2028
|
+
});
|
|
2029
|
+
return parseLoginOutput((await scope.step(() => execute(target.cliPath, ["islogin"], {
|
|
2030
|
+
pipeStdout: false,
|
|
2031
|
+
pipeStderr: false,
|
|
2032
|
+
timeout: scope.remainingMs(),
|
|
2033
|
+
signal: scope.signal
|
|
2034
|
+
}), {
|
|
2035
|
+
stage: "login-query",
|
|
2036
|
+
waitForExit: true
|
|
2037
|
+
})).stdout);
|
|
2038
|
+
}));
|
|
2039
|
+
} catch (error) {
|
|
2040
|
+
options.signal?.throwIfAborted();
|
|
2041
|
+
if (error instanceof Error && error.message.startsWith("Runtime busy:")) return {
|
|
2042
|
+
status: "unknown",
|
|
2043
|
+
reason: "runtime-busy"
|
|
2044
|
+
};
|
|
2045
|
+
if (error && typeof error === "object" && "code" in error && error.code === "WECHAT_DEVTOOLS_HOST_IDENTITY_MISMATCH") return {
|
|
2046
|
+
status: "unknown",
|
|
2047
|
+
reason: "installation-mismatch"
|
|
2048
|
+
};
|
|
2049
|
+
return {
|
|
2050
|
+
status: "unknown",
|
|
2051
|
+
reason: operation.timedOut || error && typeof error === "object" && "timedOut" in error && error.timedOut === true ? "timeout" : "command-failed"
|
|
2052
|
+
};
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
//#endregion
|
|
1421
2056
|
//#region src/cli/minidev.ts
|
|
1422
2057
|
const MINIDEV_COMMAND = "minidev";
|
|
1423
2058
|
function isCommandNotFound(error) {
|
|
@@ -1431,17 +2066,248 @@ async function runMinidev(argv) {
|
|
|
1431
2066
|
await execute(MINIDEV_COMMAND, [...argv]);
|
|
1432
2067
|
} catch (error) {
|
|
1433
2068
|
if (isCommandNotFound(error)) {
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
2069
|
+
logger.error("未检测到支付宝小程序 CLI:minidev");
|
|
2070
|
+
logger.warn("请先安装 minidev,可使用以下任一命令:");
|
|
2071
|
+
logger.info(`- ${colors.green("pnpm add -g minidev")}`);
|
|
2072
|
+
logger.info(`- ${colors.green("npm install -g minidev")}`);
|
|
2073
|
+
logger.info(`- ${colors.green("yarn global add minidev")}`);
|
|
1439
2074
|
return;
|
|
1440
2075
|
}
|
|
1441
2076
|
throw error;
|
|
1442
2077
|
}
|
|
1443
2078
|
}
|
|
1444
2079
|
//#endregion
|
|
2080
|
+
//#region src/cli/run-login-config.ts
|
|
2081
|
+
function normalizeLoginRetryMode(value, nonInteractive) {
|
|
2082
|
+
if (value && value !== "never" && value !== "once" && value !== "always") throw new Error(i18nText(`不支持的 --login-retry 值: ${value}(仅支持 never/once/always)`, `Invalid --login-retry value: ${value} (supported: never/once/always)`));
|
|
2083
|
+
if (value === "never" || value === "once" || value === "always") return value;
|
|
2084
|
+
return nonInteractive ? "never" : "always";
|
|
2085
|
+
}
|
|
2086
|
+
function normalizeLoginRetryTimeout(value) {
|
|
2087
|
+
if (!value) return 3e4;
|
|
2088
|
+
const parsed = Number.parseInt(value, 10);
|
|
2089
|
+
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)`));
|
|
2090
|
+
return parsed;
|
|
2091
|
+
}
|
|
2092
|
+
function isStdinInteractive() {
|
|
2093
|
+
return Boolean(process.stdin && process.stdin.isTTY);
|
|
2094
|
+
}
|
|
2095
|
+
function stripLoginRetryControlFlags(argv) {
|
|
2096
|
+
let next = removeOption(argv, "--login-retry");
|
|
2097
|
+
next = removeOption(next, "--login-retry-timeout");
|
|
2098
|
+
next = removeOption(next, "--non-interactive");
|
|
2099
|
+
return next;
|
|
2100
|
+
}
|
|
2101
|
+
/**
|
|
2102
|
+
* @description 解析登录重试相关控制参数,并产出实际执行微信 CLI 所需的运行时参数。
|
|
2103
|
+
*/
|
|
2104
|
+
function resolveLoginRetryConfig(argv) {
|
|
2105
|
+
const nonInteractiveFlag = argv.includes("--non-interactive");
|
|
2106
|
+
const ciMode = process.env.CI === "true";
|
|
2107
|
+
const nonTtyStdin = !isStdinInteractive();
|
|
2108
|
+
const nonInteractive = nonInteractiveFlag || ciMode || nonTtyStdin;
|
|
2109
|
+
const retryModeRaw = readOptionValue(argv, "--login-retry")?.toLowerCase();
|
|
2110
|
+
return {
|
|
2111
|
+
nonInteractive,
|
|
2112
|
+
retryMode: normalizeLoginRetryMode(retryModeRaw, nonInteractive),
|
|
2113
|
+
retryTimeoutMs: normalizeLoginRetryTimeout(readOptionValue(argv, "--login-retry-timeout")),
|
|
2114
|
+
runtimeArgv: stripLoginRetryControlFlags(argv)
|
|
2115
|
+
};
|
|
2116
|
+
}
|
|
2117
|
+
//#endregion
|
|
2118
|
+
//#region src/cli/run-login.ts
|
|
2119
|
+
const IDE_SERVER_STARTED_RE = /IDE server has started,\s*listening on\s+https?:\/\/127\.0\.0\.1:(\d+)/i;
|
|
2120
|
+
function unwrapWechatCliExecutionError(result) {
|
|
2121
|
+
return result.kind === "retryable" ? result.error : result.value;
|
|
2122
|
+
}
|
|
2123
|
+
async function promptLoginRetry(errorLike, options, retryCount, operation) {
|
|
2124
|
+
const { nonInteractive, retryMode, retryTimeoutMs } = options;
|
|
2125
|
+
if (nonInteractive) {
|
|
2126
|
+
logger.error(i18nText("当前为非交互模式,检测到登录失效后直接失败。", "Non-interactive mode enabled, failing immediately on login expiration."));
|
|
2127
|
+
return "cancel";
|
|
2128
|
+
}
|
|
2129
|
+
if (!(retryMode === "always" || retryMode === "once" && retryCount < 1)) {
|
|
2130
|
+
await promptWechatIdeLoginRetry({
|
|
2131
|
+
allowRetry: false,
|
|
2132
|
+
error: errorLike,
|
|
2133
|
+
logger,
|
|
2134
|
+
promptOpenIdeLogin: true,
|
|
2135
|
+
retryTimeoutMs: operation.remainingMs(retryTimeoutMs),
|
|
2136
|
+
signal: operation.signal
|
|
2137
|
+
});
|
|
2138
|
+
logger.info(i18nText("当前重试策略不允许继续重试。", "Current retry policy does not allow further retries."));
|
|
2139
|
+
return "cancel";
|
|
2140
|
+
}
|
|
2141
|
+
return await promptWechatIdeLoginRetry({
|
|
2142
|
+
error: errorLike,
|
|
2143
|
+
logger,
|
|
2144
|
+
promptOpenIdeLogin: true,
|
|
2145
|
+
retryTimeoutMs: operation.remainingMs(retryTimeoutMs),
|
|
2146
|
+
signal: operation.signal
|
|
2147
|
+
});
|
|
2148
|
+
}
|
|
2149
|
+
function flushExecutionOutput(result) {
|
|
2150
|
+
if (!result || typeof result !== "object") return;
|
|
2151
|
+
const candidate = result;
|
|
2152
|
+
if (typeof candidate.stdout === "string" && candidate.stdout) process.stdout.write(candidate.stdout);
|
|
2153
|
+
if (typeof candidate.stderr === "string" && candidate.stderr) process.stderr.write(candidate.stderr);
|
|
2154
|
+
}
|
|
2155
|
+
function captureWechatDevtoolsServicePort(result, target) {
|
|
2156
|
+
if (!result || typeof result !== "object") return;
|
|
2157
|
+
const candidate = result;
|
|
2158
|
+
const match = [typeof candidate.stdout === "string" ? candidate.stdout : "", typeof candidate.stderr === "string" ? candidate.stderr : ""].join("\n").match(IDE_SERVER_STARTED_RE);
|
|
2159
|
+
if (!match?.[1]) return;
|
|
2160
|
+
setRuntimeWechatDevtoolsServicePort(Number.parseInt(match[1], 10), target);
|
|
2161
|
+
}
|
|
2162
|
+
/**
|
|
2163
|
+
* @description 运行微信开发者工具 CLI,并在登录失效时允许按键重试。
|
|
2164
|
+
*/
|
|
2165
|
+
async function executeWechatCliWithRetry(cliPath, argv, options) {
|
|
2166
|
+
options.signal?.throwIfAborted();
|
|
2167
|
+
const target = await resolveWechatDevtoolsTarget({
|
|
2168
|
+
target: options.target,
|
|
2169
|
+
cliPath
|
|
2170
|
+
});
|
|
2171
|
+
const loginRetryOptions = resolveLoginRetryConfig(argv);
|
|
2172
|
+
const result = await runWithSuspendedSharedInput(async () => {
|
|
2173
|
+
return await runRetryableCommand({
|
|
2174
|
+
timeout: options.timeout,
|
|
2175
|
+
signal: options.signal,
|
|
2176
|
+
createCancelError: (result) => createWechatIdeLoginRequiredExitError(unwrapWechatCliExecutionError(result)),
|
|
2177
|
+
execute: async (operation) => {
|
|
2178
|
+
await assertWechatDevtoolsHost(target, {
|
|
2179
|
+
signal: operation.signal,
|
|
2180
|
+
timeout: operation.remainingMs()
|
|
2181
|
+
});
|
|
2182
|
+
operation.signal.throwIfAborted();
|
|
2183
|
+
try {
|
|
2184
|
+
return {
|
|
2185
|
+
kind: "result",
|
|
2186
|
+
value: await execute(target.cliPath, loginRetryOptions.runtimeArgv, {
|
|
2187
|
+
pipeStdout: false,
|
|
2188
|
+
pipeStderr: false,
|
|
2189
|
+
timeout: operation.remainingMs(),
|
|
2190
|
+
signal: operation.signal
|
|
2191
|
+
})
|
|
2192
|
+
};
|
|
2193
|
+
} catch (error) {
|
|
2194
|
+
if (!isWechatIdeLoginRequiredError(error)) throw error;
|
|
2195
|
+
return {
|
|
2196
|
+
error,
|
|
2197
|
+
kind: "retryable"
|
|
2198
|
+
};
|
|
2199
|
+
}
|
|
2200
|
+
},
|
|
2201
|
+
isRetryableResult: (result) => result.kind === "retryable" || isWechatIdeLoginRequiredError(result.value),
|
|
2202
|
+
onRetry: () => {
|
|
2203
|
+
logger.info(i18nText("正在重试连接微信开发者工具...", "Retrying to connect Wechat DevTools..."));
|
|
2204
|
+
},
|
|
2205
|
+
promptRetry: async (result, retryCount, operation) => {
|
|
2206
|
+
return await promptLoginRetry(unwrapWechatCliExecutionError(result), loginRetryOptions, retryCount, operation);
|
|
2207
|
+
},
|
|
2208
|
+
shouldRetry: (action) => action === "retry"
|
|
2209
|
+
});
|
|
2210
|
+
});
|
|
2211
|
+
if (result.kind === "retryable") throw createWechatIdeLoginRequiredExitError(result.error);
|
|
2212
|
+
if (isWechatIdeLoginRequiredError(result.value)) throw createWechatIdeLoginRequiredExitError(result.value);
|
|
2213
|
+
captureWechatDevtoolsServicePort(result.value, target);
|
|
2214
|
+
if (!options.silent) flushExecutionOutput(result.value);
|
|
2215
|
+
}
|
|
2216
|
+
/** 原生 CLI 可能启动或改变共享宿主,完整命令与有限重试共用机器租约。 */
|
|
2217
|
+
async function runWechatCliWithRetry(cliPath, argv, options = {}) {
|
|
2218
|
+
return await withMachineE2ELease(() => executeWechatCliWithRetry(cliPath, argv, options));
|
|
2219
|
+
}
|
|
2220
|
+
//#endregion
|
|
2221
|
+
//#region src/cli/run-wechat-cli.ts
|
|
2222
|
+
function shouldBootstrapWechatDevtools(command) {
|
|
2223
|
+
return command === "open" || command === "auto" || command === "auto-preview";
|
|
2224
|
+
}
|
|
2225
|
+
function appendOptionValue(argv, sourceArgv, optionName) {
|
|
2226
|
+
const value = readOptionValue(sourceArgv, optionName);
|
|
2227
|
+
if (value !== void 0) argv.push(optionName, value);
|
|
2228
|
+
}
|
|
2229
|
+
function createAutoPreviewWakeArgv(argv, trustProject) {
|
|
2230
|
+
if (argv[0] !== "auto-preview") return;
|
|
2231
|
+
const projectPath = readOptionValue(argv, "--project");
|
|
2232
|
+
const appid = readOptionValue(argv, "--appid");
|
|
2233
|
+
if (!projectPath && !appid) return;
|
|
2234
|
+
const wakeArgv = ["open"];
|
|
2235
|
+
appendOptionValue(wakeArgv, argv, "--project");
|
|
2236
|
+
appendOptionValue(wakeArgv, argv, "--appid");
|
|
2237
|
+
appendOptionValue(wakeArgv, argv, "--ext-appid");
|
|
2238
|
+
if (trustProject === true) wakeArgv.push("--trust-project");
|
|
2239
|
+
return wakeArgv;
|
|
2240
|
+
}
|
|
2241
|
+
function resolveBooleanCliOption(argv, optionName) {
|
|
2242
|
+
if (argv.includes(optionName)) return true;
|
|
2243
|
+
const rawValue = readOptionValue(argv, optionName);
|
|
2244
|
+
if (rawValue === void 0) return;
|
|
2245
|
+
const normalized = rawValue.trim().toLowerCase();
|
|
2246
|
+
if (normalized === "" || normalized === "true" || normalized === "1" || normalized === "on") return true;
|
|
2247
|
+
if (normalized === "false" || normalized === "0" || normalized === "off") return false;
|
|
2248
|
+
return true;
|
|
2249
|
+
}
|
|
2250
|
+
async function handleMissingCliPath(source) {
|
|
2251
|
+
if (source === "environment") throw new Error("WEAPP_IDE_CLI_PATH 指定的开发者工具不可用;已停止,不回退其他安装。");
|
|
2252
|
+
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.`);
|
|
2253
|
+
logger.warn(message);
|
|
2254
|
+
await promptForCliPath();
|
|
2255
|
+
}
|
|
2256
|
+
async function maybeBootstrapWechatDevtoolsSettings(argv, target) {
|
|
2257
|
+
const command = argv[0];
|
|
2258
|
+
if (!shouldBootstrapWechatDevtools(command)) return;
|
|
2259
|
+
const config = await readCustomConfig();
|
|
2260
|
+
if (config.autoBootstrapDevtools === false) return;
|
|
2261
|
+
const projectPath = readOptionValue(argv, "--project");
|
|
2262
|
+
const trustProjectOption = resolveBooleanCliOption(argv, "--trust-project");
|
|
2263
|
+
const trustProject = trustProjectOption === void 0 ? config.autoTrustProject ?? false : trustProjectOption;
|
|
2264
|
+
await bootstrapWechatDevtoolsSettings({
|
|
2265
|
+
target,
|
|
2266
|
+
projectPath,
|
|
2267
|
+
trustProject
|
|
2268
|
+
});
|
|
2269
|
+
return { trustProject };
|
|
2270
|
+
}
|
|
2271
|
+
/** 在动作开始前选择安装,沿用原有平台检查和缺失路径配置提示。 */
|
|
2272
|
+
async function resolveWechatCliCommandTarget(options = {}) {
|
|
2273
|
+
if (!isOperatingSystemSupported(operatingSystemName)) {
|
|
2274
|
+
logger.warn(i18nText(`微信web开发者工具不支持当前平台:${operatingSystemName} !`, `Wechat Web DevTools CLI is not supported on current platform: ${operatingSystemName}!`));
|
|
2275
|
+
return;
|
|
2276
|
+
}
|
|
2277
|
+
let target = options.target;
|
|
2278
|
+
if (!target) {
|
|
2279
|
+
const { cliPath, source } = await resolveCliPath();
|
|
2280
|
+
if (!cliPath) {
|
|
2281
|
+
await handleMissingCliPath(source);
|
|
2282
|
+
return;
|
|
2283
|
+
}
|
|
2284
|
+
target = await resolveWechatDevtoolsTarget({ cliPath });
|
|
2285
|
+
}
|
|
2286
|
+
return target;
|
|
2287
|
+
}
|
|
2288
|
+
/**
|
|
2289
|
+
* @description 执行微信开发者工具 CLI 阶段,包括环境检查、路径解析、bootstrap 与登录重试。
|
|
2290
|
+
*/
|
|
2291
|
+
async function executeWechatCliCommand(argv, options) {
|
|
2292
|
+
const target = await resolveWechatCliCommandTarget(options);
|
|
2293
|
+
if (!target) return;
|
|
2294
|
+
await assertWechatDevtoolsHost(target, {
|
|
2295
|
+
signal: options.signal,
|
|
2296
|
+
timeout: options.timeout
|
|
2297
|
+
});
|
|
2298
|
+
const executionOptions = {
|
|
2299
|
+
...options,
|
|
2300
|
+
target
|
|
2301
|
+
};
|
|
2302
|
+
const wakeArgv = createAutoPreviewWakeArgv(argv, (await maybeBootstrapWechatDevtoolsSettings(argv, target))?.trustProject);
|
|
2303
|
+
if (wakeArgv) await runWechatCliWithRetry(target.cliPath, wakeArgv, executionOptions);
|
|
2304
|
+
await runWechatCliWithRetry(target.cliPath, argv, executionOptions);
|
|
2305
|
+
}
|
|
2306
|
+
/** 配置预热与原生命令属于同一个宿主操作,排他租约在任何写入之前取得。 */
|
|
2307
|
+
async function runWechatCliCommand(argv, options = {}) {
|
|
2308
|
+
return await withMachineE2ELease(() => executeWechatCliCommand(argv, options));
|
|
2309
|
+
}
|
|
2310
|
+
//#endregion
|
|
1445
2311
|
//#region src/cli/wechat-command-schema.ts
|
|
1446
2312
|
const CACHE_CLEAN_TYPES = [
|
|
1447
2313
|
"storage",
|
|
@@ -1526,6 +2392,299 @@ function validateWechatCliCommandArgs(argv) {
|
|
|
1526
2392
|
}
|
|
1527
2393
|
}
|
|
1528
2394
|
//#endregion
|
|
2395
|
+
//#region src/cli/wechat-commands.ts
|
|
2396
|
+
const DEVTOOLS_SHUTDOWN_TIMEOUT_MS = 1e4;
|
|
2397
|
+
function appendProjectLocatorArgv(argv, options) {
|
|
2398
|
+
if (options.projectPath) argv.push("--project", path.resolve(options.projectPath));
|
|
2399
|
+
if (options.appid) argv.push("--appid", options.appid);
|
|
2400
|
+
if (options.extAppid) argv.push("--ext-appid", options.extAppid);
|
|
2401
|
+
}
|
|
2402
|
+
/**
|
|
2403
|
+
* @description 调用微信开发者工具 open 命令。
|
|
2404
|
+
*/
|
|
2405
|
+
async function openSelectedWechatIde(options, target) {
|
|
2406
|
+
const argv = ["open"];
|
|
2407
|
+
appendProjectLocatorArgv(argv, options);
|
|
2408
|
+
if (options.platform) argv.push("--platform", options.platform);
|
|
2409
|
+
if (options.trustProject) argv.push("--trust-project");
|
|
2410
|
+
if (options.projectPath) try {
|
|
2411
|
+
await openWechatIdeProjectByHttp(options.projectPath, { target });
|
|
2412
|
+
return;
|
|
2413
|
+
} catch (error) {
|
|
2414
|
+
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;
|
|
2415
|
+
}
|
|
2416
|
+
await runWechatCliCommand(argv, { target });
|
|
2417
|
+
}
|
|
2418
|
+
/** 打开与兼容回退在同一机器租约内沿用固定安装,禁止重新选择宿主。 */
|
|
2419
|
+
async function openWechatIde(options = {}) {
|
|
2420
|
+
return await withMachineE2ELease(async () => {
|
|
2421
|
+
const target = await resolveWechatCliCommandTarget();
|
|
2422
|
+
if (target) {
|
|
2423
|
+
if (process.env["WEAPP_IDE_MANAGED_PROJECT_JOURNAL"]?.trim()) {
|
|
2424
|
+
if (!options.projectPath) throw new Error("Managed DevTools open requires an explicit project path; no project was opened.");
|
|
2425
|
+
await ensureManagedWechatProject(target, options.projectPath, { trustProject: options.trustProject });
|
|
2426
|
+
return;
|
|
2427
|
+
}
|
|
2428
|
+
return await openSelectedWechatIde(options, target);
|
|
2429
|
+
}
|
|
2430
|
+
});
|
|
2431
|
+
}
|
|
2432
|
+
/**
|
|
2433
|
+
* @description 调用微信开发者工具 login 命令。
|
|
2434
|
+
*/
|
|
2435
|
+
async function loginWechatIde(options = {}) {
|
|
2436
|
+
const argv = ["login"];
|
|
2437
|
+
if (options.qrFormat) argv.push("--qr-format", options.qrFormat);
|
|
2438
|
+
if (options.qrOutput) argv.push("--qr-output", path.resolve(options.qrOutput));
|
|
2439
|
+
if (options.qrSize) argv.push("--qr-size", options.qrSize);
|
|
2440
|
+
if (options.resultOutput) argv.push("--result-output", path.resolve(options.resultOutput));
|
|
2441
|
+
await runWechatCliCommand(argv);
|
|
2442
|
+
}
|
|
2443
|
+
/**
|
|
2444
|
+
* @description 调用微信开发者工具 islogin 命令。
|
|
2445
|
+
*/
|
|
2446
|
+
async function isWechatIdeLoggedIn(options = {}) {
|
|
2447
|
+
const argv = ["islogin"];
|
|
2448
|
+
if (options.nonInteractive) argv.push("--non-interactive");
|
|
2449
|
+
if (options.silent) await runWechatCliCommand(argv, { silent: true });
|
|
2450
|
+
else await runWechatCliCommand(argv);
|
|
2451
|
+
}
|
|
2452
|
+
/**
|
|
2453
|
+
* @description 调用微信开发者工具 build-npm 命令。
|
|
2454
|
+
*/
|
|
2455
|
+
async function buildWechatIdeNpm(options = {}) {
|
|
2456
|
+
const argv = [
|
|
2457
|
+
"build-npm",
|
|
2458
|
+
"--project",
|
|
2459
|
+
path.resolve(options.projectPath ?? process.cwd())
|
|
2460
|
+
];
|
|
2461
|
+
if (options.compileType) argv.push("--compile-type", options.compileType);
|
|
2462
|
+
await runWechatCliCommand(argv);
|
|
2463
|
+
}
|
|
2464
|
+
/**
|
|
2465
|
+
* @description 调用微信开发者工具 preview 命令。
|
|
2466
|
+
*/
|
|
2467
|
+
async function previewWechatIde(options = {}) {
|
|
2468
|
+
const argv = ["preview"];
|
|
2469
|
+
appendProjectLocatorArgv(argv, options);
|
|
2470
|
+
if (options.qrFormat) argv.push("--qr-format", options.qrFormat);
|
|
2471
|
+
if (options.qrOutput) argv.push("--qr-output", path.resolve(options.qrOutput));
|
|
2472
|
+
if (options.qrSize) argv.push("--qr-size", options.qrSize);
|
|
2473
|
+
if (options.infoOutput) argv.push("--info-output", path.resolve(options.infoOutput));
|
|
2474
|
+
if (options.compileCondition) argv.push("--compile-condition", options.compileCondition);
|
|
2475
|
+
await runWechatCliCommand(argv);
|
|
2476
|
+
}
|
|
2477
|
+
/**
|
|
2478
|
+
* @description 调用微信开发者工具 auto-preview 命令。
|
|
2479
|
+
*/
|
|
2480
|
+
async function autoPreviewWechatIde(options = {}) {
|
|
2481
|
+
const argv = ["auto-preview"];
|
|
2482
|
+
appendProjectLocatorArgv(argv, options);
|
|
2483
|
+
if (options.infoOutput) argv.push("--info-output", path.resolve(options.infoOutput));
|
|
2484
|
+
if (options.compileCondition) argv.push("--compile-condition", options.compileCondition);
|
|
2485
|
+
await runWechatCliCommand(argv);
|
|
2486
|
+
}
|
|
2487
|
+
/**
|
|
2488
|
+
* @description 调用微信开发者工具 auto 命令。
|
|
2489
|
+
*/
|
|
2490
|
+
async function autoWechatIde(options = {}) {
|
|
2491
|
+
const argv = ["auto"];
|
|
2492
|
+
appendProjectLocatorArgv(argv, options);
|
|
2493
|
+
if (options.port) argv.push("--auto-port", options.port);
|
|
2494
|
+
if (options.account) argv.push("--auto-account", options.account);
|
|
2495
|
+
if (options.testTicket) argv.push("--test-ticket", options.testTicket);
|
|
2496
|
+
if (options.ticket) argv.push("--ticket", options.ticket);
|
|
2497
|
+
if (options.trustProject) argv.push("--trust-project");
|
|
2498
|
+
await runWechatCliCommand(argv);
|
|
2499
|
+
}
|
|
2500
|
+
/**
|
|
2501
|
+
* @description 调用微信开发者工具 auto-replay 命令。
|
|
2502
|
+
*/
|
|
2503
|
+
async function autoReplayWechatIde(options = {}) {
|
|
2504
|
+
const argv = ["auto-replay"];
|
|
2505
|
+
appendProjectLocatorArgv(argv, options);
|
|
2506
|
+
if (options.port) argv.push("--auto-port", options.port);
|
|
2507
|
+
if (options.account) argv.push("--auto-account", options.account);
|
|
2508
|
+
if (options.replayAll) argv.push("--replay-all");
|
|
2509
|
+
if (options.replayConfigPath) argv.push("--replay-config-path", path.resolve(options.replayConfigPath));
|
|
2510
|
+
if (options.testTicket) argv.push("--test-ticket", options.testTicket);
|
|
2511
|
+
if (options.ticket) argv.push("--ticket", options.ticket);
|
|
2512
|
+
if (options.trustProject) argv.push("--trust-project");
|
|
2513
|
+
await runWechatCliCommand(argv);
|
|
2514
|
+
}
|
|
2515
|
+
/**
|
|
2516
|
+
* @description 调用微信开发者工具 upload 命令。
|
|
2517
|
+
*/
|
|
2518
|
+
async function uploadWechatIde(options) {
|
|
2519
|
+
const argv = ["upload"];
|
|
2520
|
+
appendProjectLocatorArgv(argv, options);
|
|
2521
|
+
argv.push("--version", options.version, "--desc", options.desc);
|
|
2522
|
+
if (options.infoOutput) argv.push("--info-output", path.resolve(options.infoOutput));
|
|
2523
|
+
await runWechatCliCommand(argv);
|
|
2524
|
+
}
|
|
2525
|
+
/**
|
|
2526
|
+
* @description 调用微信开发者工具 close 命令。
|
|
2527
|
+
*/
|
|
2528
|
+
async function closeWechatIdeProject(options = {}) {
|
|
2529
|
+
if (options.projectPath !== void 0 && !options.projectPath.trim()) throw new Error("The explicit close projectPath must not be empty.");
|
|
2530
|
+
if (options.cliPath !== void 0 && !options.cliPath.trim()) throw new Error("The explicit close cliPath must not be empty.");
|
|
2531
|
+
const argv = ["close"];
|
|
2532
|
+
appendProjectLocatorArgv(argv, options);
|
|
2533
|
+
const target = options.target || options.cliPath ? await resolveWechatDevtoolsTarget({
|
|
2534
|
+
target: options.target,
|
|
2535
|
+
cliPath: options.cliPath
|
|
2536
|
+
}) : void 0;
|
|
2537
|
+
await runWechatCliCommand(argv, {
|
|
2538
|
+
...target ? { target } : {},
|
|
2539
|
+
timeout: DEVTOOLS_SHUTDOWN_TIMEOUT_MS
|
|
2540
|
+
});
|
|
2541
|
+
}
|
|
2542
|
+
/**
|
|
2543
|
+
* @description 调用微信开发者工具 quit 命令。
|
|
2544
|
+
*/
|
|
2545
|
+
async function quitWechatIde() {
|
|
2546
|
+
await runWechatCliCommand(["quit"], { timeout: DEVTOOLS_SHUTDOWN_TIMEOUT_MS });
|
|
2547
|
+
}
|
|
2548
|
+
/**
|
|
2549
|
+
* @description 调用微信开发者工具 cache 命令。
|
|
2550
|
+
*/
|
|
2551
|
+
async function clearWechatIdeCache(options) {
|
|
2552
|
+
await runWechatCliCommand([
|
|
2553
|
+
"cache",
|
|
2554
|
+
"--clean",
|
|
2555
|
+
options.clean
|
|
2556
|
+
]);
|
|
2557
|
+
}
|
|
2558
|
+
/**
|
|
2559
|
+
* @description 调用微信开发者工具 open-other 命令。
|
|
2560
|
+
*/
|
|
2561
|
+
async function openWechatIdeOtherProject(_options = {}) {
|
|
2562
|
+
await runWechatCliCommand(["open-other"]);
|
|
2563
|
+
}
|
|
2564
|
+
/**
|
|
2565
|
+
* @description 通过微信开发者工具 HTTP 服务端口重置指定项目的 fileutils 状态。
|
|
2566
|
+
*/
|
|
2567
|
+
async function resetWechatIdeFileUtils(options) {
|
|
2568
|
+
await resetWechatIdeFileUtilsByHttp(path.resolve(options.projectPath));
|
|
2569
|
+
}
|
|
2570
|
+
function createAutomatorSessionOptions(options) {
|
|
2571
|
+
return {
|
|
2572
|
+
...options.port ? { port: options.port } : {},
|
|
2573
|
+
preferOpenedSession: options.preferOpenedSession ?? true,
|
|
2574
|
+
preserveProjectRoot: options.preserveProjectRoot,
|
|
2575
|
+
projectPath: path.resolve(options.projectPath),
|
|
2576
|
+
...options.sessionId ? { sessionId: options.sessionId } : {},
|
|
2577
|
+
sharedSession: options.sharedSession ?? true,
|
|
2578
|
+
timeout: options.timeout
|
|
2579
|
+
};
|
|
2580
|
+
}
|
|
2581
|
+
/**
|
|
2582
|
+
* @description 通过已打开或新建的 automator 会话获取开发者工具基础信息。
|
|
2583
|
+
*/
|
|
2584
|
+
async function getWechatIdeToolInfo(options) {
|
|
2585
|
+
return await withMiniProgram$1(createAutomatorSessionOptions(options), async (miniProgram) => {
|
|
2586
|
+
return await miniProgram.toolInfo();
|
|
2587
|
+
});
|
|
2588
|
+
}
|
|
2589
|
+
/**
|
|
2590
|
+
* @description 通过已打开或新建的 automator 会话执行项目编译。
|
|
2591
|
+
*/
|
|
2592
|
+
async function compileWechatIdeByAutomator(options) {
|
|
2593
|
+
return await withMiniProgram$1(createAutomatorSessionOptions(options), async (miniProgram) => {
|
|
2594
|
+
return await miniProgram.compile({ force: options.force });
|
|
2595
|
+
});
|
|
2596
|
+
}
|
|
2597
|
+
/**
|
|
2598
|
+
* @description 通过已打开或新建的 automator 会话清理开发者工具缓存。
|
|
2599
|
+
*/
|
|
2600
|
+
async function clearWechatIdeCacheByAutomator(options) {
|
|
2601
|
+
return await withMiniProgram$1(createAutomatorSessionOptions(options), async (miniProgram) => {
|
|
2602
|
+
return await miniProgram.clearCache({ clean: options.clean });
|
|
2603
|
+
});
|
|
2604
|
+
}
|
|
2605
|
+
/**
|
|
2606
|
+
* @description 通过已打开或新建的 automator 会话获取当前 ticket。
|
|
2607
|
+
*/
|
|
2608
|
+
async function getWechatIdeTicket(options) {
|
|
2609
|
+
return await withMiniProgram$1(createAutomatorSessionOptions(options), async (miniProgram) => {
|
|
2610
|
+
return await miniProgram.getTicket();
|
|
2611
|
+
});
|
|
2612
|
+
}
|
|
2613
|
+
/**
|
|
2614
|
+
* @description 通过已打开或新建的 automator 会话设置 ticket。
|
|
2615
|
+
*/
|
|
2616
|
+
async function setWechatIdeTicket(options) {
|
|
2617
|
+
return await withMiniProgram$1(createAutomatorSessionOptions(options), async (miniProgram) => {
|
|
2618
|
+
await miniProgram.setTicket(options.ticket);
|
|
2619
|
+
});
|
|
2620
|
+
}
|
|
2621
|
+
/**
|
|
2622
|
+
* @description 通过已打开或新建的 automator 会话刷新 ticket。
|
|
2623
|
+
*/
|
|
2624
|
+
async function refreshWechatIdeTicket(options) {
|
|
2625
|
+
return await withMiniProgram$1(createAutomatorSessionOptions(options), async (miniProgram) => {
|
|
2626
|
+
await miniProgram.refreshTicket();
|
|
2627
|
+
});
|
|
2628
|
+
}
|
|
2629
|
+
/**
|
|
2630
|
+
* @description 通过已打开或新建的 automator 会话获取测试账号列表。
|
|
2631
|
+
*/
|
|
2632
|
+
async function getWechatIdeTestAccounts(options) {
|
|
2633
|
+
return await withMiniProgram$1(createAutomatorSessionOptions(options), async (miniProgram) => {
|
|
2634
|
+
return await miniProgram.testAccounts();
|
|
2635
|
+
});
|
|
2636
|
+
}
|
|
2637
|
+
/**
|
|
2638
|
+
* @description 调用微信开发者工具 build-apk 命令。
|
|
2639
|
+
*/
|
|
2640
|
+
async function buildWechatIdeApk(options) {
|
|
2641
|
+
const argv = [
|
|
2642
|
+
"build-apk",
|
|
2643
|
+
"--key-store",
|
|
2644
|
+
path.resolve(options.keyStore),
|
|
2645
|
+
"--key-alias",
|
|
2646
|
+
options.keyAlias,
|
|
2647
|
+
"--key-pass",
|
|
2648
|
+
options.keyPass,
|
|
2649
|
+
"--store-pass",
|
|
2650
|
+
options.storePass,
|
|
2651
|
+
"--output",
|
|
2652
|
+
path.resolve(options.output)
|
|
2653
|
+
];
|
|
2654
|
+
if (options.useAab !== void 0) argv.push("--use-aab", String(options.useAab));
|
|
2655
|
+
if (options.desc) argv.push("--desc", options.desc);
|
|
2656
|
+
if (options.isUploadResourceBundle) argv.push("--isUploadResourceBundle");
|
|
2657
|
+
if (options.resourceBundleVersion) argv.push("--resourceBundleVersion", options.resourceBundleVersion);
|
|
2658
|
+
if (options.resourceBundleDesc) argv.push("--resourceBundleDesc", options.resourceBundleDesc);
|
|
2659
|
+
await runWechatCliCommand(argv);
|
|
2660
|
+
}
|
|
2661
|
+
/**
|
|
2662
|
+
* @description 调用微信开发者工具 build-ipa 命令。
|
|
2663
|
+
*/
|
|
2664
|
+
async function buildWechatIdeIpa(options) {
|
|
2665
|
+
const argv = [
|
|
2666
|
+
"build-ipa",
|
|
2667
|
+
"--output",
|
|
2668
|
+
path.resolve(options.output),
|
|
2669
|
+
"--isDistribute",
|
|
2670
|
+
String(options.isDistribute)
|
|
2671
|
+
];
|
|
2672
|
+
if (options.isRemoteBuild !== void 0) argv.push("--isRemoteBuild", String(options.isRemoteBuild));
|
|
2673
|
+
if (options.profilePath) argv.push("--profilePath", path.resolve(options.profilePath));
|
|
2674
|
+
if (options.certificateName) argv.push("--certificateName", options.certificateName);
|
|
2675
|
+
if (options.p12Path) argv.push("--p12Path", path.resolve(options.p12Path));
|
|
2676
|
+
if (options.p12Password) argv.push("--p12Password", options.p12Password);
|
|
2677
|
+
if (options.tpnsProfilePath) argv.push("--tpnsProfilePath", path.resolve(options.tpnsProfilePath));
|
|
2678
|
+
if (options.isUploadBeta !== void 0) argv.push("--isUploadBeta", String(options.isUploadBeta));
|
|
2679
|
+
if (options.isUploadResourceBundle) argv.push("--isUploadResourceBundle");
|
|
2680
|
+
if (options.resourceBundleVersion) argv.push("--resourceBundleVersion", options.resourceBundleVersion);
|
|
2681
|
+
if (options.resourceBundleDesc) argv.push("--resourceBundleDesc", options.resourceBundleDesc);
|
|
2682
|
+
if (options.versionName) argv.push("--versionName", options.versionName);
|
|
2683
|
+
if (options.versionCode !== void 0) argv.push("--versionCode", String(options.versionCode));
|
|
2684
|
+
if (options.versionDesc) argv.push("--versionDesc", options.versionDesc);
|
|
2685
|
+
await runWechatCliCommand(argv);
|
|
2686
|
+
}
|
|
2687
|
+
//#endregion
|
|
1529
2688
|
//#region src/cli/wechat-dispatch.ts
|
|
1530
2689
|
function hasOption(argv, optionName) {
|
|
1531
2690
|
return argv.includes(optionName) || argv.some((token) => token.startsWith(`${optionName}=`));
|
|
@@ -1676,7 +2835,15 @@ async function dispatchWechatCliCommand(argv) {
|
|
|
1676
2835
|
return true;
|
|
1677
2836
|
}
|
|
1678
2837
|
if (command === "close") {
|
|
1679
|
-
|
|
2838
|
+
const projectPath = readOptionValue(argv, "--project", "-p");
|
|
2839
|
+
const cliPath = readOptionValue(argv, "--cli-path");
|
|
2840
|
+
if ((hasOption(argv, "--project") || hasOption(argv, "-p")) && (!projectPath || projectPath.startsWith("-"))) throw new Error("The close --project/-p option requires a value.");
|
|
2841
|
+
if (hasOption(argv, "--cli-path") && (!cliPath || cliPath.startsWith("-"))) throw new Error("The close --cli-path option requires a value.");
|
|
2842
|
+
if (projectPath !== void 0 || cliPath !== void 0) await closeWechatIdeProject({
|
|
2843
|
+
...projectPath !== void 0 ? { projectPath } : {},
|
|
2844
|
+
...cliPath !== void 0 ? { cliPath } : {}
|
|
2845
|
+
});
|
|
2846
|
+
else await closeWechatIdeProject();
|
|
1680
2847
|
return true;
|
|
1681
2848
|
}
|
|
1682
2849
|
if (command === "quit") {
|
|
@@ -1806,7 +2973,7 @@ async function parse(argv) {
|
|
|
1806
2973
|
return;
|
|
1807
2974
|
}
|
|
1808
2975
|
if (matchedCommand === "mcp") {
|
|
1809
|
-
const { runMcpCommand } = await import("./run-mcp-
|
|
2976
|
+
const { runMcpCommand } = await import("./run-mcp-CPv8cesh.js");
|
|
1810
2977
|
await runMcpCommand(argv.slice(1));
|
|
1811
2978
|
return;
|
|
1812
2979
|
}
|
|
@@ -1824,4 +2991,4 @@ async function parse(argv) {
|
|
|
1824
2991
|
await runWechatCliCommand(formattedArgv);
|
|
1825
2992
|
}
|
|
1826
2993
|
//#endregion
|
|
1827
|
-
export {
|
|
2994
|
+
export { MINIDEV_NAMESPACE_COMMAND_NAMES as $, queryWechatIdeLogin as A, WECHAT_DEVTOOLS_ENGINE_BUILD_STATUSES as B, refreshWechatIdeTicket as C, validateWechatCliCommandArgs as D, uploadWechatIde as E, startForwardConsole as F, startWechatIdeEngineBuildByHttp as G, pollWechatIdeEngineBuildResultByHttp as H, isWechatIdeEngineBuildEndpointMissingError as I, handleConfigCommand as J, getRuntimeWechatDevtoolsServicePort as K, prepareAcceptanceProject as L, createAlias as M, createPathCompat as N, runWechatCliWithRetry as O, transformArgv as P, MCP_COMMAND_NAME as Q, runWechatIdeEngineBuild as R, quitWechatIde as S, setWechatIdeTicket as T, requestWechatDevtoolsHttp as U, openWechatIdeProjectByHttp as V, resetWechatIdeFileUtilsByHttp as W, promptForCliPath as X, resolveCliPath as Y, CONFIG_COMMAND_NAME as Z, isWechatIdeLoggedIn as _, autoReplayWechatIde as a, isAutomatorCommand as at, openWechatIdeOtherProject as b, buildWechatIdeIpa as c, printScreenshotHelp as ct, clearWechatIdeCacheByAutomator as d, parseAutomatorArgs as dt, WEAPP_IDE_TOP_LEVEL_COMMAND_NAMES as et, closeWechatIdeProject as f, readBooleanOption as ft, getWechatIdeToolInfo as g, getWechatIdeTicket as h, autoPreviewWechatIde as i, getAutomatorCommandHelp as it, execute as j, runMinidev as k, buildWechatIdeNpm as l, parseCompareArgs as lt, getWechatIdeTestAccounts as m, removeOption as mt, parse as n, isWeappIdeTopLevelCommand as nt, autoWechatIde as o, runAutomatorCommand as ot, compileWechatIdeByAutomator as p, readOptionValue as pt, setRuntimeWechatDevtoolsServicePort as q, dispatchWechatCliCommand as r, AUTOMATOR_COMMAND_NAMES as rt, buildWechatIdeApk as s, parseScreenshotArgs as st, createCli as t, WECHAT_CLI_COMMAND_NAMES as tt, clearWechatIdeCache as u, printCompareHelp as ut, loginWechatIde as v, resetWechatIdeFileUtils as w, previewWechatIde as x, openWechatIde as y, runWechatIdeEngineBuildByHttp as z };
|