weapp-ide-cli 6.1.9 → 6.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,212 @@
1
+ import { a as assertWechatDevtoolsPort, t as persistAutomatorSession } from "./sessionStore-zdzy2UFM.js";
2
+ import { h as startWechatIdeAgent, t as beginManagedWechatProject } from "./devtoolsProjectOwnership-yDD6h0dO.js";
3
+ import path from "node:path";
4
+ import { withMachineE2ELease } from "@weapp-vite/devtools-runtime";
5
+ import { execa } from "execa";
6
+ import fs from "node:fs/promises";
7
+ import process from "node:process";
8
+ import { acquireAutomatorPortLease } from "@weapp-vite/miniprogram-automator";
9
+ //#region src/cli/projectImport.ts
10
+ function isRecord(value) {
11
+ return typeof value === "object" && value !== null && !Array.isArray(value);
12
+ }
13
+ /** 使用同一安装的 skill 入口;Windows 由 execa 处理官方 cmd 包装器。 */
14
+ function resolveProjectImportCli(cliPath, platform = process.platform) {
15
+ const paths = platform === "win32" ? path.win32 : path.posix;
16
+ return paths.join(paths.dirname(cliPath), platform === "win32" ? "wechatide.cmd" : "wechatide");
17
+ }
18
+ /** 官方导入会重写项目根、条件页和基础库选择;窗口启动前恢复原始输入。 */
19
+ async function preserveProjectConfig(projectPath, fileName, run) {
20
+ const file = path.join(projectPath, fileName);
21
+ const original = await fs.readFile(file).catch((error) => {
22
+ if (error.code !== "ENOENT") throw error;
23
+ });
24
+ let failure;
25
+ let failed = false;
26
+ try {
27
+ await run();
28
+ } catch (error) {
29
+ failure = error;
30
+ failed = true;
31
+ }
32
+ try {
33
+ if (original) {
34
+ if (!(await fs.readFile(file).catch((error) => {
35
+ if (error.code !== "ENOENT") throw error;
36
+ }))?.equals(original)) await fs.writeFile(file, original);
37
+ } else await fs.rm(file, { force: true });
38
+ } catch (error) {
39
+ if (failed) throw new AggregateError([failure, error], "Official import failed and fixture config restoration did not complete");
40
+ throw error;
41
+ }
42
+ if (failed) throw failure;
43
+ }
44
+ /** 先完成官方项目导入,再启动受管窗口;导入不编译、不导航,也不修改私有能力记录。 */
45
+ async function importManagedDevtoolsProject(options) {
46
+ if (!options.trusted || !process.env["WEAPP_IDE_MANAGED_PROJECT_JOURNAL"]) return;
47
+ options.signal.throwIfAborted();
48
+ const projectPath = path.resolve(options.projectPath);
49
+ await withMachineE2ELease(() => preserveProjectConfig(projectPath, "project.config.json", () => preserveProjectConfig(projectPath, "project.private.config.json", async () => {
50
+ options.signal.throwIfAborted();
51
+ const { stdout } = await execa(resolveProjectImportCli(options.cliPath), [
52
+ "-c",
53
+ "weapp-vite-e2e",
54
+ "project_import",
55
+ "--project",
56
+ projectPath
57
+ ], {
58
+ timeout: options.timeout,
59
+ cancelSignal: options.signal,
60
+ killDescendants: false
61
+ });
62
+ const payload = JSON.parse(stdout);
63
+ const result = isRecord(payload) && isRecord(payload.result) ? payload.result : void 0;
64
+ if (!isRecord(payload) || payload.ok !== true || payload.tool !== "project_import" || payload.clientName !== "weapp-vite-e2e" || result?.success !== true || result.projectPath !== projectPath || typeof result.alreadyImported !== "boolean" || result.taskId !== void 0 || result.status !== void 0 || result.error !== void 0) throw new Error("DEVTOOLS_PROJECT_IMPORT_INCOMPLETE: 官方项目导入未完成;请检查本地 weapp-vite-e2e 客户端授权与导入结果,未启动项目窗口。");
65
+ options.signal.throwIfAborted();
66
+ })));
67
+ }
68
+ //#endregion
69
+ //#region src/cli/automator/managed.ts
70
+ /** 受管启动持有完整窗口回执,失败清理不受启动 deadline 的短暂宽限限制。 */
71
+ async function launchManagedAutomator(options) {
72
+ const { launcher, scope, target, projectPath, sourceProjectPath, port } = options;
73
+ await scope.step(() => importManagedDevtoolsProject({
74
+ cliPath: target.cliPath,
75
+ projectPath,
76
+ trusted: options.trustProject,
77
+ timeout: scope.remainingMs(),
78
+ signal: scope.signal
79
+ }), { stage: "project-import" });
80
+ scope.throwIfAborted();
81
+ const portLease = await scope.step(() => acquireAutomatorPortLease(port), {
82
+ stage: "port-lease",
83
+ disposeLate: (lease) => lease.release()
84
+ });
85
+ const rawReleasePortLease = portLease.release.bind(portLease);
86
+ let releasedPortLease;
87
+ portLease.release = () => releasedPortLease ??= rawReleasePortLease();
88
+ const disownPortLease = scope.own(() => portLease.release(), "automator-port-lease");
89
+ let releasePortLeaseOnExit = true;
90
+ let intent;
91
+ let program;
92
+ try {
93
+ intent = await beginManagedWechatProject({
94
+ target,
95
+ projectPath,
96
+ port
97
+ });
98
+ if (!intent) throw new Error("DEVTOOLS_MANAGED_PROJECT_JOURNAL_REQUIRED: managed launch requires a project ownership journal.");
99
+ scope.throwIfAborted();
100
+ await startWechatIdeAgent({
101
+ target,
102
+ projectPath,
103
+ port,
104
+ trustProject: options.trustProject,
105
+ signal: scope.signal,
106
+ timeout: scope.remainingMs(),
107
+ onStarted: async (result) => await intent.confirm({
108
+ openedProjectWindow: result.openedProjectWindow,
109
+ port: result.autoPort
110
+ })
111
+ });
112
+ scope.throwIfAborted();
113
+ await assertWechatDevtoolsPort(target, port, {
114
+ signal: scope.signal,
115
+ timeout: scope.remainingMs()
116
+ });
117
+ scope.throwIfAborted();
118
+ const wsEndpoint = `ws://127.0.0.1:${port}`;
119
+ program = await launcher.connect({
120
+ wsEndpoint,
121
+ signal: scope.signal,
122
+ timeout: scope.remainingMs()
123
+ });
124
+ scope.throwIfAborted();
125
+ await program.waitForAppReady(scope.remainingMs());
126
+ scope.throwIfAborted();
127
+ Reflect.set(program, "__WEAPP_VITE_SESSION_METADATA", {
128
+ port,
129
+ projectPath,
130
+ wsEndpoint,
131
+ managedProject: {
132
+ id: intent.id,
133
+ journalPath: intent.journalPath
134
+ }
135
+ });
136
+ await persistAutomatorSession({
137
+ installationId: target.installationId,
138
+ signal: scope.signal,
139
+ port,
140
+ projectPath: sourceProjectPath,
141
+ sessionId: options.sessionId,
142
+ wsEndpoint
143
+ });
144
+ scope.throwIfAborted();
145
+ if (options.persistAsDefaultSession) {
146
+ await persistAutomatorSession({
147
+ installationId: target.installationId,
148
+ signal: scope.signal,
149
+ projectPath: sourceProjectPath,
150
+ wsEndpoint
151
+ });
152
+ scope.throwIfAborted();
153
+ }
154
+ const connectedProgram = program;
155
+ const ownedIntent = intent;
156
+ const rawDisconnect = connectedProgram.disconnect.bind(connectedProgram);
157
+ connectedProgram.disconnect = (...args) => {
158
+ try {
159
+ return rawDisconnect(...args);
160
+ } finally {
161
+ portLease.release();
162
+ }
163
+ };
164
+ let closing;
165
+ connectedProgram.close = async () => {
166
+ closing ??= (async () => {
167
+ try {
168
+ connectedProgram.disconnect();
169
+ } finally {
170
+ try {
171
+ await ownedIntent.close();
172
+ } finally {
173
+ await portLease.release();
174
+ }
175
+ }
176
+ })().finally(() => {
177
+ closing = void 0;
178
+ });
179
+ return await closing;
180
+ };
181
+ releasePortLeaseOnExit = false;
182
+ disownPortLease();
183
+ return connectedProgram;
184
+ } catch (error) {
185
+ const errors = [error];
186
+ try {
187
+ program?.disconnect();
188
+ } catch (disconnectError) {
189
+ errors.push(disconnectError);
190
+ }
191
+ if (intent) {
192
+ try {
193
+ await intent.fail(error);
194
+ } catch (recordError) {
195
+ errors.push(recordError);
196
+ }
197
+ try {
198
+ await intent.close();
199
+ } catch (closeError) {
200
+ errors.push(closeError);
201
+ }
202
+ }
203
+ if (releasePortLeaseOnExit) {
204
+ disownPortLease();
205
+ await portLease.release().catch((releaseError) => errors.push(releaseError));
206
+ }
207
+ if (errors.length > 1) throw new AggregateError(errors, "Managed DevTools launch failed and project cleanup did not complete.", { cause: error });
208
+ throw error;
209
+ }
210
+ }
211
+ //#endregion
212
+ export { launchManagedAutomator };
@@ -0,0 +1 @@
1
+ export { t as runMcpCommand } from "./run-mcp-Dl_1TH1x.js";
@@ -1,11 +1,9 @@
1
- import { c as withMiniProgram$1 } from "./automator-session-fbanL2eW.js";
2
- import { Buffer } from "node:buffer";
3
- import fs from "node:fs/promises";
1
+ import { c as withMiniProgram$1 } from "./automator-session-gWEsSs_A.js";
4
2
  import path from "node:path";
5
- import process from "node:process";
6
3
  import { readDevtoolsElementSnapshot as readElementSnapshot, resolveDevtoolsProjectPath as resolveProjectPath, toDevtoolsSerializableValue as toSerializableValue } from "@weapp-vite/devtools-runtime";
7
- import { serveStdio } from "@modelcontextprotocol/server/stdio";
8
- import { McpServer } from "@modelcontextprotocol/server";
4
+ import fs from "node:fs/promises";
5
+ import process from "node:process";
6
+ import { Buffer } from "node:buffer";
9
7
  import { z } from "zod";
10
8
  //#region src/mcp/server.ts
11
9
  function createResolvedProjectPath(workspaceRoot, projectPath) {
@@ -16,6 +14,7 @@ function createResolvedOutputPath(workspaceRoot, outputPath) {
16
14
  }
17
15
  async function withConnectedMiniProgram(runtimeHooks, workspaceRoot, input, runner) {
18
16
  return await runtimeHooks.withMiniProgram({
17
+ cliPath: input.cliPath,
19
18
  preferOpenedSession: input.preferOpenedSession,
20
19
  port: input.port,
21
20
  projectPath: createResolvedProjectPath(workspaceRoot, input.projectPath),
@@ -31,9 +30,10 @@ async function withConnectedPage(runtimeHooks, workspaceRoot, input, runner) {
31
30
  }
32
31
  function defineConnectionSchema() {
33
32
  return {
33
+ cliPath: z.string().trim().min(1).optional().describe("指定微信开发者工具 CLI 安装路径"),
34
34
  projectPath: z.string().trim().min(1).describe("小程序项目路径,支持 workspaceRoot 相对路径"),
35
35
  timeout: z.number().int().positive().optional(),
36
- port: z.number().int().positive().optional(),
36
+ port: z.number().int().positive().max(65535).optional(),
37
37
  preferOpenedSession: z.boolean().optional(),
38
38
  sessionId: z.string().trim().min(1).optional()
39
39
  };
@@ -367,6 +367,7 @@ function registerWeappIdeMcpTools(server, options) {
367
367
  });
368
368
  }
369
369
  async function createWeappIdeMcpServer(options) {
370
+ const { McpServer } = await import("@modelcontextprotocol/server");
370
371
  const server = new McpServer({
371
372
  name: "weapp-ide-cli",
372
373
  version: "1.0.0"
@@ -380,8 +381,12 @@ async function createWeappIdeMcpServer(options) {
380
381
  //#endregion
381
382
  //#region src/mcp/runtime.ts
382
383
  async function startWeappIdeMcpServer(options = {}) {
384
+ const { serveStdio } = await import("@modelcontextprotocol/server/stdio");
383
385
  const serverOptions = {
384
- runtimeHooks: { withMiniProgram: withMiniProgram$1 },
386
+ runtimeHooks: { withMiniProgram: (input, runner) => withMiniProgram$1({
387
+ ...input,
388
+ cliPath: input.cliPath ?? options.cliPath
389
+ }, runner) },
385
390
  workspaceRoot: options.workspaceRoot
386
391
  };
387
392
  const handle = serveStdio(async () => {
@@ -415,6 +420,7 @@ function printMcpHelp() {
415
420
  Start the weapp-ide-cli MCP server over stdio.
416
421
 
417
422
  Options:
423
+ --cli-path <path> Select the WeChat DevTools installation
418
424
  --workspace-root <path> Resolve relative project/output paths from this root
419
425
  -h, --help Show this help
420
426
 
@@ -434,7 +440,10 @@ async function runMcpCommand(argv) {
434
440
  printMcpHelp();
435
441
  return;
436
442
  }
437
- await startWeappIdeMcpServer({ workspaceRoot: readOptionValue(argv, "--workspace-root") });
443
+ await startWeappIdeMcpServer({
444
+ cliPath: readOptionValue(argv, "--cli-path"),
445
+ workspaceRoot: readOptionValue(argv, "--workspace-root")
446
+ });
438
447
  }
439
448
  //#endregion
440
449
  export { readElementSnapshot as a, registerWeappIdeMcpTools as i, startWeappIdeMcpServer as n, resolveProjectPath as o, createWeappIdeMcpServer as r, toSerializableValue as s, runMcpCommand as t };