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.
- package/README.md +74 -0
- package/dist/automator-BOe32upt.js +822 -0
- package/dist/automator-VSjgHujY.js +1 -0
- package/dist/automator-session-VOB03glx.js +186 -0
- package/dist/automatorProject-DjYLqMj6.js +881 -0
- package/dist/{cli-2sY1sAVq.js → cli-CFlqFpIw.js} +1552 -74
- package/dist/cli.js +3 -3
- package/dist/commands-DmirGBy4.js +1 -0
- package/dist/commands-mslf7-sP.js +529 -0
- package/dist/devtoolsProjectOwnership-BMfSAg4n.js +2022 -0
- package/dist/index.d.ts +420 -76
- package/dist/index.js +8 -5
- package/dist/managed-B04Xo9U_.js +264 -0
- package/dist/run-mcp-83-VKZVA.js +1 -0
- package/dist/{run-mcp-LqaWlQJ_.js → run-mcp-Ni139Z5C.js} +28 -9
- package/package.json +13 -12
- 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 { 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
|
|
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-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
|
|
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 子命令。
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1321
|
+
logger.info(i18nText("配置诊断结果:", "Configuration diagnostics:"));
|
|
986
1322
|
console.log(JSON.stringify(report, null, 2));
|
|
987
|
-
if (!hasConfigFile)
|
|
988
|
-
if (hasCustomCli && !hasValidCli)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1098
|
-
|
|
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
|
|
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
|
|
1818
|
+
async function runSelectedWechatIdeEngineBuild(projectPath, options = {}) {
|
|
1151
1819
|
const logs = [];
|
|
1152
1820
|
let lastLoggedMessage;
|
|
1153
1821
|
try {
|
|
1154
|
-
const result = await
|
|
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
|
-
|
|
1981
|
+
logger.error(line);
|
|
1229
1982
|
return;
|
|
1230
1983
|
case "warn":
|
|
1231
|
-
|
|
1984
|
+
logger.warn(line);
|
|
1232
1985
|
return;
|
|
1233
1986
|
case "info":
|
|
1234
|
-
|
|
1987
|
+
logger.info(line);
|
|
1235
1988
|
return;
|
|
1236
|
-
default:
|
|
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
|
-
|
|
1323
|
-
|
|
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
|
-
|
|
1380
|
-
|
|
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
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
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
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
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
|
-
|
|
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
|
|
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-
|
|
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 {
|
|
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 };
|