weapp-ide-cli 5.4.9 → 5.4.11

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.
@@ -650,7 +650,8 @@ function normalizeAutomatorSessionId(sessionId, port) {
650
650
  * @description 为项目路径派生稳定的 DevTools automator 端口,避免多个 dev:open 项目抢占默认端口。
651
651
  */
652
652
  function resolveProjectAutomatorPort(projectPath) {
653
- return PROJECT_AUTOMATOR_PORT_BASE + createHash("sha1").update(path.resolve(projectPath)).digest().readUInt32BE(0) % PROJECT_AUTOMATOR_PORT_RANGE;
653
+ const offset = createHash("sha1").update(path.resolve(projectPath)).digest().readUInt32BE(0) % PROJECT_AUTOMATOR_PORT_RANGE;
654
+ return PROJECT_AUTOMATOR_PORT_BASE + offset;
654
655
  }
655
656
  function resolveAutomatorSessionFilePath(projectPath, sessionId, port) {
656
657
  const normalizedProjectPath = path.resolve(projectPath);
@@ -816,12 +817,15 @@ async function launchAutomator(options) {
816
817
  * @description 连接当前项目已打开的开发者工具自动化会话,不触发新的 IDE 拉起。
817
818
  */
818
819
  async function connectOpenedAutomator(options) {
819
- const { port, projectPath, sessionId } = options;
820
+ const { port, projectPath, sessionId, timeout } = options;
820
821
  const launcher = new Launcher();
821
822
  const persistedSession = await readPersistedAutomatorSession(projectPath, sessionId, port);
822
823
  const wsEndpoint = persistedSession?.wsEndpoint ?? (port ? `ws://127.0.0.1:${port}` : `ws://127.0.0.1:${DEFAULT_WECHAT_DEVTOOLS_WS_PORT}`);
823
824
  try {
824
- return await launcher.connect({ wsEndpoint });
825
+ return await launcher.connect({
826
+ timeout,
827
+ wsEndpoint
828
+ });
825
829
  } catch (error) {
826
830
  if (persistedSession) await removePersistedAutomatorSession(projectPath, sessionId, port);
827
831
  throw error;
@@ -1,6 +1,6 @@
1
- import { $ as overwriteCustomConfig, C as configureLocaleFromArgv, H as resolveDevtoolsAutomationDefaults, J as logger_default, Q as createLocaleConfig, T as validateLocaleOption, V as getConfiguredLocale, X as createAutoTrustProjectConfig, Y as createAutoBootstrapDevtoolsConfig, Z as createCustomConfig, a as releaseSharedMiniProgram, et as readCustomConfig, q as colors, r as connectMiniProgram, rt as defaultCustomConfigFilePath, t as acquireSharedMiniProgram, tt as removeCustomConfigKey, w as i18nText, z as resolveCliPath } from "./automator-session-C8JdS2rx.js";
2
- import { A as isWechatIdeLoggedIn, B as runWechatCliCommand, C as clearWechatIdeCache, F as quitWechatIde, G as transformArgv, H as execute, K as promptForCliPath, L as resetWechatIdeFileUtils, M as openWechatIde, N as openWechatIdeOtherProject, P as previewWechatIde, Q as startWechatIdeEngineBuildByHttp, S as buildWechatIdeNpm, T as closeWechatIdeProject, U as createAlias, W as createPathCompat, Y as pollWechatIdeEngineBuildResultByHttp, _ as autoPreviewWechatIde, a as navigateBack, b as buildWechatIdeApk, c as pageStack, d as remote, f as scrollTo, g as tap, i as input, it as removeOption, j as loginWechatIde, l as reLaunch, m as systemInfo, n as captureScreenshotBuffer, nt as readBooleanOption, o as navigateTo, p as switchTab, r as currentPage, rt as readOptionValue, s as pageData, t as audit, tt as parseAutomatorArgs, u as redirectTo, v as autoReplayWechatIde, x as buildWechatIdeIpa, y as autoWechatIde, z as uploadWechatIde } from "./commands-C6ATyJuz.js";
3
- import "./run-mcp-X00XjC8Z.js";
1
+ import { $ as overwriteCustomConfig, C as configureLocaleFromArgv, H as resolveDevtoolsAutomationDefaults, J as logger_default, Q as createLocaleConfig, T as validateLocaleOption, V as getConfiguredLocale, X as createAutoTrustProjectConfig, Y as createAutoBootstrapDevtoolsConfig, Z as createCustomConfig, a as releaseSharedMiniProgram, et as readCustomConfig, q as colors, r as connectMiniProgram, rt as defaultCustomConfigFilePath, t as acquireSharedMiniProgram, tt as removeCustomConfigKey, w as i18nText, z as resolveCliPath } from "./automator-session-ec0i76Cx.js";
2
+ import { A as isWechatIdeLoggedIn, B as runWechatCliCommand, C as clearWechatIdeCache, F as quitWechatIde, G as transformArgv, H as execute, K as promptForCliPath, L as resetWechatIdeFileUtils, M as openWechatIde, N as openWechatIdeOtherProject, P as previewWechatIde, Q as startWechatIdeEngineBuildByHttp, S as buildWechatIdeNpm, T as closeWechatIdeProject, U as createAlias, W as createPathCompat, Y as pollWechatIdeEngineBuildResultByHttp, _ as autoPreviewWechatIde, a as navigateBack, b as buildWechatIdeApk, c as pageStack, d as remote, f as scrollTo, g as tap, i as input, it as removeOption, j as loginWechatIde, l as reLaunch, m as systemInfo, n as captureScreenshotBuffer, nt as readBooleanOption, o as navigateTo, p as switchTab, r as currentPage, rt as readOptionValue, s as pageData, t as audit, tt as parseAutomatorArgs, u as redirectTo, v as autoReplayWechatIde, x as buildWechatIdeIpa, y as autoWechatIde, z as uploadWechatIde } from "./commands-CK4N5Nel.js";
3
+ import "./run-mcp-DPz-bwrD.js";
4
4
  import fs from "node:fs/promises";
5
5
  import path from "node:path";
6
6
  import { fs as fs$1 } from "@weapp-core/shared/fs";
@@ -313,7 +313,7 @@ async function runScreenshot(argv) {
313
313
  }
314
314
  const options = parseScreenshotArgs(argv);
315
315
  const isJsonOutput = argv.includes("--json");
316
- const { takeScreenshot } = await import("./commands-CEJ6rVma.js");
316
+ const { takeScreenshot } = await import("./commands-DKiKnVKp.js");
317
317
  const result = await takeScreenshot(options);
318
318
  if (isJsonOutput) {
319
319
  console.log(JSON.stringify(result, null, 2));
@@ -1725,7 +1725,7 @@ async function parse(argv) {
1725
1725
  return;
1726
1726
  }
1727
1727
  if (matchedCommand === "mcp") {
1728
- const { runMcpCommand } = await import("./run-mcp-B5RshzSs.js");
1728
+ const { runMcpCommand } = await import("./run-mcp-BFYPend3.js");
1729
1729
  await runMcpCommand(argv.slice(1));
1730
1730
  return;
1731
1731
  }
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
- import { J as logger_default } from "./automator-session-C8JdS2rx.js";
2
- import { n as parse } from "./cli-BUx9wWVK.js";
1
+ import { J as logger_default } from "./automator-session-ec0i76Cx.js";
2
+ import { n as parse } from "./cli-BBXXj7vb.js";
3
3
  import process from "node:process";
4
4
  //#region src/cli.ts
5
5
  const argv = process.argv.slice(2);
@@ -1,4 +1,4 @@
1
- import { G as isOperatingSystemSupported, J as logger_default, K as operatingSystemName, L as bootstrapWechatDevtoolsSettings, R as detectWechatDevtoolsServicePort, Z as createCustomConfig, d as createWechatIdeLoginRequiredExitError, et as readCustomConfig, h as isWechatIdeLoginRequiredError, it as resolvePath, n as closeSharedMiniProgram, o as withMiniProgram, q as colors, rt as defaultCustomConfigFilePath, s as runRetryableCommand, v as promptWechatIdeLoginRetry, w as i18nText, x as runWithSuspendedSharedInput, z as resolveCliPath } from "./automator-session-C8JdS2rx.js";
1
+ import { G as isOperatingSystemSupported, J as logger_default, K as operatingSystemName, L as bootstrapWechatDevtoolsSettings, R as detectWechatDevtoolsServicePort, Z as createCustomConfig, d as createWechatIdeLoginRequiredExitError, et as readCustomConfig, h as isWechatIdeLoginRequiredError, it as resolvePath, n as closeSharedMiniProgram, o as withMiniProgram, q as colors, rt as defaultCustomConfigFilePath, s as runRetryableCommand, v as promptWechatIdeLoginRetry, w as i18nText, x as runWithSuspendedSharedInput, z as resolveCliPath } from "./automator-session-ec0i76Cx.js";
2
2
  import { Buffer } from "node:buffer";
3
3
  import fs from "node:fs/promises";
4
4
  import path from "node:path";
@@ -166,6 +166,8 @@ function removeOption(argv, optionName) {
166
166
  }
167
167
  //#endregion
168
168
  //#region src/cli/fullPageScreenshot.ts
169
+ const MAX_FULL_PAGE_CAPTURES = 200;
170
+ const DEVTOOLS_INSPECTEE_REFERENCE_ERROR = /__inspectee__ is not defined|Can't find variable:\s*__inspectee__/i;
169
171
  function decodeScreenshotBuffer(raw) {
170
172
  if (raw === void 0) throw new Error("Failed to capture screenshot");
171
173
  const buffer = typeof raw === "string" ? Buffer.from(raw, "base64") : Buffer.from(raw);
@@ -175,13 +177,16 @@ function decodeScreenshotBuffer(raw) {
175
177
  function toPositiveNumber(value) {
176
178
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
177
179
  }
178
- function createScrollPositions(totalHeight, viewportHeight) {
179
- if (totalHeight <= viewportHeight) return [0];
180
- const positions = [];
181
- const lastStart = Math.max(totalHeight - viewportHeight, 0);
182
- for (let scrollTop = 0; scrollTop < lastStart; scrollTop += viewportHeight) positions.push(scrollTop);
183
- if (positions[positions.length - 1] !== lastStart) positions.push(lastStart);
184
- return positions;
180
+ function isDevtoolsInspecteeReferenceError(error) {
181
+ const message = error instanceof Error ? error.message : String(error);
182
+ return DEVTOOLS_INSPECTEE_REFERENCE_ERROR.test(message);
183
+ }
184
+ async function captureViewportScreenshotBuffer(miniProgram, timeoutMs, runWithTimeout, screenshotTimeoutMessage) {
185
+ return decodeScreenshotBuffer(await runWithTimeout(miniProgram.screenshot({ timeout: timeoutMs }), timeoutMs, screenshotTimeoutMessage, "DEVTOOLS_SCREENSHOT_TIMEOUT"));
186
+ }
187
+ function resolveScale(pageWidth, viewportHeight, png) {
188
+ if (pageWidth && pageWidth > 0) return png.width / pageWidth;
189
+ return png.height / viewportHeight;
185
190
  }
186
191
  function cropPngRows(source, startRow, rowCount) {
187
192
  const cropped = new PNG({
@@ -196,9 +201,90 @@ function cropPngRows(source, startRow, rowCount) {
196
201
  }
197
202
  return cropped;
198
203
  }
204
+ function areRowsVisuallyEqual(first, second, firstRow, secondRow) {
205
+ const tolerance = 6;
206
+ const requiredEqualRatio = .98;
207
+ let equalPixels = 0;
208
+ for (let column = 0; column < first.width; column += 1) {
209
+ const firstIndex = (firstRow * first.width + column) * 4;
210
+ const secondIndex = (secondRow * second.width + column) * 4;
211
+ if (Math.abs(first.data[firstIndex] - second.data[secondIndex]) <= tolerance && Math.abs(first.data[firstIndex + 1] - second.data[secondIndex + 1]) <= tolerance && Math.abs(first.data[firstIndex + 2] - second.data[secondIndex + 2]) <= tolerance && Math.abs(first.data[firstIndex + 3] - second.data[secondIndex + 3]) <= tolerance) equalPixels += 1;
212
+ }
213
+ return equalPixels / first.width >= requiredEqualRatio;
214
+ }
215
+ function detectFixedBottomRows(first, second, scale) {
216
+ if (first.width !== second.width || first.height !== second.height) return 0;
217
+ const maxFixedRows = Math.floor(first.height * .45);
218
+ const minFixedRows = Math.max(8, Math.round(8 * scale));
219
+ let fixedRows = 0;
220
+ for (let offset = 0; offset < maxFixedRows; offset += 1) {
221
+ if (!areRowsVisuallyEqual(first, second, first.height - 1 - offset, second.height - 1 - offset)) break;
222
+ fixedRows += 1;
223
+ }
224
+ return fixedRows >= minFixedRows ? fixedRows : 0;
225
+ }
199
226
  async function restoreScrollPosition(miniProgram, page, scrollTop) {
227
+ try {
228
+ await miniProgram.pageScrollTo(scrollTop);
229
+ await page.waitFor(150);
230
+ } catch (error) {
231
+ if (!isDevtoolsInspecteeReferenceError(error)) throw error;
232
+ }
233
+ }
234
+ async function readRuntimeViewportMetrics(miniProgram) {
235
+ if (typeof miniProgram.evaluate !== "function") return;
236
+ const metrics = await miniProgram.evaluate(() => {
237
+ const wxLike = typeof wx === "undefined" ? void 0 : wx;
238
+ const systemInfo = wxLike?.getWindowInfo?.() ?? wxLike?.getSystemInfoSync?.();
239
+ return new Promise((resolve) => {
240
+ if (!wxLike?.createSelectorQuery) {
241
+ resolve({
242
+ pageHeight: void 0,
243
+ pageWidth: systemInfo?.windowWidth,
244
+ scrollTop: void 0,
245
+ viewportHeight: systemInfo?.windowHeight
246
+ });
247
+ return;
248
+ }
249
+ wxLike.createSelectorQuery().selectViewport().scrollOffset((viewport) => {
250
+ resolve({
251
+ pageHeight: viewport?.scrollHeight,
252
+ pageWidth: viewport?.scrollWidth ?? systemInfo?.windowWidth,
253
+ scrollTop: viewport?.scrollTop,
254
+ viewportHeight: systemInfo?.windowHeight
255
+ });
256
+ }).exec();
257
+ });
258
+ }).catch(() => void 0);
259
+ if (!metrics || typeof metrics !== "object") return;
260
+ const raw = metrics;
261
+ return {
262
+ pageHeight: toPositiveNumber(raw.pageHeight),
263
+ pageWidth: toPositiveNumber(raw.pageWidth),
264
+ scrollTop: typeof raw.scrollTop === "number" && Number.isFinite(raw.scrollTop) ? Math.max(raw.scrollTop, 0) : void 0,
265
+ viewportHeight: toPositiveNumber(raw.viewportHeight)
266
+ };
267
+ }
268
+ async function readPageSize(page) {
269
+ return await page.size().catch(() => null);
270
+ }
271
+ async function readScrollTop(miniProgram, page) {
272
+ const runtimeMetrics = await readRuntimeViewportMetrics(miniProgram);
273
+ if (runtimeMetrics?.scrollTop !== void 0) return runtimeMetrics.scrollTop;
274
+ return typeof page.scrollTop === "function" ? toPositiveNumber(await page.scrollTop().catch(() => void 0)) ?? 0 : 0;
275
+ }
276
+ async function captureScrolledPng(miniProgram, page, scrollTop, timeoutMs, runWithTimeout, screenshotTimeoutMessage) {
200
277
  await miniProgram.pageScrollTo(scrollTop);
201
278
  await page.waitFor(150);
279
+ const rawScreenshot = await runWithTimeout(miniProgram.screenshot({ timeout: timeoutMs }), timeoutMs, screenshotTimeoutMessage, "DEVTOOLS_SCREENSHOT_TIMEOUT");
280
+ const runtimeMetrics = await readRuntimeViewportMetrics(miniProgram);
281
+ const actualScrollTop = runtimeMetrics?.scrollTop ?? (typeof page.scrollTop === "function" ? toPositiveNumber(await page.scrollTop().catch(() => void 0)) ?? scrollTop : scrollTop);
282
+ const pageSize = runtimeMetrics?.pageHeight ? void 0 : await readPageSize(page);
283
+ return {
284
+ pageHeight: runtimeMetrics?.pageHeight ?? toPositiveNumber(pageSize?.height),
285
+ png: PNG.sync.read(decodeScreenshotBuffer(rawScreenshot)),
286
+ scrollTop: actualScrollTop
287
+ };
202
288
  }
203
289
  /**
204
290
  * @description 通过多次滚动和拼接生成整页长截图。
@@ -206,32 +292,75 @@ async function restoreScrollPosition(miniProgram, page, scrollTop) {
206
292
  async function captureFullPageScreenshotBuffer(options) {
207
293
  const { miniProgram, timeoutMs, runWithTimeout, screenshotTimeoutMessage } = options;
208
294
  const page = await miniProgram.currentPage();
209
- const pageSize = await page.size();
210
- const systemInfo = await miniProgram.systemInfo();
211
- const initialScrollTop = typeof page.scrollTop === "function" ? await page.scrollTop() : 0;
212
- const pageHeight = toPositiveNumber(pageSize.height);
213
- const viewportHeight = toPositiveNumber(systemInfo.windowHeight);
214
- if (!pageHeight || !viewportHeight) return decodeScreenshotBuffer(await runWithTimeout(miniProgram.screenshot({ timeout: timeoutMs }), timeoutMs, screenshotTimeoutMessage, "DEVTOOLS_SCREENSHOT_TIMEOUT"));
295
+ const runtimeMetrics = await readRuntimeViewportMetrics(miniProgram);
296
+ const pageSize = runtimeMetrics?.pageHeight && runtimeMetrics.pageWidth ? void 0 : await readPageSize(page);
297
+ const systemInfo = runtimeMetrics?.viewportHeight ? void 0 : await miniProgram.systemInfo().catch((error) => {
298
+ if (isDevtoolsInspecteeReferenceError(error)) return;
299
+ throw error;
300
+ });
301
+ const initialScrollTop = runtimeMetrics?.scrollTop ?? await readScrollTop(miniProgram, page);
302
+ const pageWidth = runtimeMetrics?.pageWidth ?? toPositiveNumber(pageSize?.width);
303
+ const pageHeight = runtimeMetrics?.pageHeight ?? toPositiveNumber(pageSize?.height);
304
+ const viewportHeight = runtimeMetrics?.viewportHeight ?? toPositiveNumber(systemInfo?.windowHeight);
305
+ if (!pageHeight || !viewportHeight) return await captureViewportScreenshotBuffer(miniProgram, timeoutMs, runWithTimeout, screenshotTimeoutMessage);
215
306
  const segments = [];
216
- const positions = createScrollPositions(pageHeight, viewportHeight);
307
+ const capturesByRequestedScrollTop = /* @__PURE__ */ new Map();
217
308
  let coveredUntil = 0;
218
309
  let scale = 1;
310
+ let fixedBottomRows = 0;
311
+ let contentViewportHeight = viewportHeight;
312
+ let latestPageHeight = pageHeight;
219
313
  try {
220
- for (const scrollTop of positions) {
221
- await miniProgram.pageScrollTo(scrollTop);
222
- await page.waitFor(150);
223
- const rawScreenshot = await runWithTimeout(miniProgram.screenshot({ timeout: timeoutMs }), timeoutMs, screenshotTimeoutMessage, "DEVTOOLS_SCREENSHOT_TIMEOUT");
224
- const png = PNG.sync.read(decodeScreenshotBuffer(rawScreenshot));
225
- if (viewportHeight > 0) scale = png.height / viewportHeight;
226
- const visibleEnd = Math.min(scrollTop + viewportHeight, pageHeight);
314
+ const firstCapture = await captureScrolledPng(miniProgram, page, 0, timeoutMs, runWithTimeout, screenshotTimeoutMessage);
315
+ const firstPng = firstCapture.png;
316
+ capturesByRequestedScrollTop.set(0, firstCapture);
317
+ scale = resolveScale(pageWidth, viewportHeight, firstPng);
318
+ latestPageHeight = firstCapture.pageHeight ?? latestPageHeight;
319
+ if (pageHeight > viewportHeight) {
320
+ const probeScrollTop = Math.min(viewportHeight, Math.max(pageHeight - viewportHeight, 1));
321
+ const probeCapture = await captureScrolledPng(miniProgram, page, probeScrollTop, timeoutMs, runWithTimeout, screenshotTimeoutMessage);
322
+ const probePng = probeCapture.png;
323
+ capturesByRequestedScrollTop.set(probeScrollTop, probeCapture);
324
+ latestPageHeight = probeCapture.pageHeight ?? latestPageHeight;
325
+ fixedBottomRows = detectFixedBottomRows(firstPng, probePng, scale);
326
+ if (fixedBottomRows > 0) {
327
+ const screenshotCssHeight = firstPng.height / scale;
328
+ const fixedBottomCss = fixedBottomRows / scale;
329
+ const fixedRowsOutsideViewportCss = Math.max(screenshotCssHeight - viewportHeight, 0);
330
+ const fixedRowsInsideViewportCss = Math.max(fixedBottomCss - fixedRowsOutsideViewportCss, 0);
331
+ contentViewportHeight = Math.max(1, viewportHeight - fixedRowsInsideViewportCss);
332
+ }
333
+ }
334
+ let requestedScrollTop = 0;
335
+ for (let captureIndex = 0; captureIndex < MAX_FULL_PAGE_CAPTURES; captureIndex += 1) {
336
+ const capture = capturesByRequestedScrollTop.get(requestedScrollTop) ?? await captureScrolledPng(miniProgram, page, requestedScrollTop, timeoutMs, runWithTimeout, screenshotTimeoutMessage);
337
+ capturesByRequestedScrollTop.set(requestedScrollTop, capture);
338
+ const { png, scrollTop } = capture;
339
+ latestPageHeight = capture.pageHeight ?? latestPageHeight;
340
+ const visibleEnd = Math.min(scrollTop + contentViewportHeight, latestPageHeight);
227
341
  const cropTopCss = Math.max(coveredUntil - scrollTop, 0);
228
342
  const segmentHeightCss = Math.max(visibleEnd - scrollTop - cropTopCss, 0);
229
- if (segmentHeightCss <= 0) continue;
230
- const cropTopRows = Math.min(Math.max(Math.round(cropTopCss * scale), 0), png.height);
231
- const segmentRows = Math.min(Math.max(Math.round(segmentHeightCss * scale), 1), png.height - cropTopRows);
232
- segments.push(cropPngRows(png, cropTopRows, segmentRows));
233
- coveredUntil = visibleEnd;
343
+ const isAtBottom = visibleEnd >= latestPageHeight - 1;
344
+ if (segmentHeightCss > 0) {
345
+ const contentRows = Math.max(png.height - fixedBottomRows, 1);
346
+ const cropTopRows = Math.min(Math.max(Math.round(cropTopCss * scale), 0), contentRows);
347
+ const segmentRows = Math.min(Math.max(Math.round(segmentHeightCss * scale), 1), contentRows - cropTopRows);
348
+ if (segmentRows > 0) segments.push(cropPngRows(png, cropTopRows, segmentRows));
349
+ }
350
+ coveredUntil = Math.max(coveredUntil, visibleEnd);
351
+ if (isAtBottom) {
352
+ if (fixedBottomRows > 0) segments.push(cropPngRows(png, png.height - fixedBottomRows, fixedBottomRows));
353
+ break;
354
+ }
355
+ if (visibleEnd <= scrollTop || coveredUntil <= requestedScrollTop) {
356
+ if (fixedBottomRows > 0) segments.push(cropPngRows(png, png.height - fixedBottomRows, fixedBottomRows));
357
+ break;
358
+ }
359
+ requestedScrollTop = coveredUntil;
234
360
  }
361
+ } catch (error) {
362
+ if (!isDevtoolsInspecteeReferenceError(error)) throw error;
363
+ return await captureViewportScreenshotBuffer(miniProgram, timeoutMs, runWithTimeout, screenshotTimeoutMessage);
235
364
  } finally {
236
365
  await restoreScrollPosition(miniProgram, page, initialScrollTop);
237
366
  }
@@ -935,6 +1064,9 @@ function isDevtoolsProtocolTimeoutError(error) {
935
1064
  if (!(error instanceof Error)) return false;
936
1065
  return error.message === "DEVTOOLS_PROTOCOL_TIMEOUT" || error.message.includes("DevTools did not respond to protocol method") || Reflect.get(error, "code") === "DEVTOOLS_PROTOCOL_TIMEOUT";
937
1066
  }
1067
+ function isPageStackStaleError(error) {
1068
+ return error instanceof Error && error.message.includes("page is not on top of page stack");
1069
+ }
938
1070
  function isScreenshotNavigationTimeoutError(error) {
939
1071
  return error instanceof Error && Reflect.get(error, "code") === "DEVTOOLS_SCREENSHOT_NAVIGATION_TIMEOUT";
940
1072
  }
@@ -972,10 +1104,20 @@ function printStructuredResult(result, json, title) {
972
1104
  logger_default.info(title);
973
1105
  console.log(JSON.stringify(result, null, 2));
974
1106
  }
975
- async function requireElement(page, selector) {
976
- const element = await page.$(selector);
977
- if (!element) throw new Error(i18nText(`未找到元素: ${selector}`, `Element not found: ${selector}`));
978
- return element;
1107
+ async function requireElement(page, selector, timeoutMs = 5e3) {
1108
+ const startedAt = Date.now();
1109
+ let lastError;
1110
+ while (Date.now() - startedAt <= timeoutMs) {
1111
+ try {
1112
+ const element = await page.$(selector);
1113
+ if (element) return element;
1114
+ } catch (error) {
1115
+ lastError = error;
1116
+ }
1117
+ await sleep(160);
1118
+ }
1119
+ const reason = lastError instanceof Error ? ` ${lastError.message}` : "";
1120
+ throw new Error(i18nText(`未找到元素: ${selector}${reason}`, `Element not found: ${selector}${reason}`));
979
1121
  }
980
1122
  /**
981
1123
  * @description 执行保留栈页面跳转。
@@ -1055,7 +1197,17 @@ async function pageData(options) {
1055
1197
  async function tap(options) {
1056
1198
  await withMiniProgram(options, async (miniProgram) => {
1057
1199
  logger_default.info(i18nText(`正在点击元素 ${colors.cyan(options.selector)}...`, `Tapping element ${colors.cyan(options.selector)}...`));
1058
- await (await requireElement(await miniProgram.currentPage(), options.selector)).tap();
1200
+ const commandTimeout = options.timeout ?? 3e4;
1201
+ const tapCurrentPageElement = async () => {
1202
+ await (await requireElement(await miniProgram.currentPage(), options.selector, commandTimeout)).tap();
1203
+ };
1204
+ try {
1205
+ await tapCurrentPageElement();
1206
+ } catch (error) {
1207
+ if (!isPageStackStaleError(error)) throw error;
1208
+ logger_default.info(i18nText("页面栈已切换,正在重新获取顶部页面并重试点击...", "Page stack changed. Refreshing the top page and retrying the tap..."));
1209
+ await tapCurrentPageElement();
1210
+ }
1059
1211
  logger_default.success(i18nText(`已点击元素 ${colors.cyan(options.selector)}`, `Tapped element ${colors.cyan(options.selector)}`));
1060
1212
  });
1061
1213
  }
@@ -1065,7 +1217,8 @@ async function tap(options) {
1065
1217
  async function input(options) {
1066
1218
  await withMiniProgram(options, async (miniProgram) => {
1067
1219
  logger_default.info(i18nText(`正在向 ${colors.cyan(options.selector)} 输入 "${colors.cyan(options.value)}"...`, `Inputting "${colors.cyan(options.value)}" into ${colors.cyan(options.selector)}...`));
1068
- const element = await requireElement(await miniProgram.currentPage(), options.selector);
1220
+ const commandTimeout = options.timeout ?? 3e4;
1221
+ const element = await requireElement(await miniProgram.currentPage(), options.selector, commandTimeout);
1069
1222
  if (typeof element.input !== "function") throw new TypeError(i18nText(`元素不支持输入: ${options.selector}`, `Element does not support input: ${options.selector}`));
1070
1223
  await element.input(options.value);
1071
1224
  logger_default.success(i18nText(`已向 ${colors.cyan(options.selector)} 输入 "${colors.cyan(options.value)}"`, `Inputted "${colors.cyan(options.value)}" into ${colors.cyan(options.selector)}`));
@@ -1147,7 +1300,7 @@ async function takeScreenshot(options) {
1147
1300
  const isProtocolTimeout = error instanceof Error && error.message === "DEVTOOLS_PROTOCOL_TIMEOUT";
1148
1301
  const isNavigationTimeout = isScreenshotNavigationTimeoutError(error);
1149
1302
  const isScreenshotTimeout = isScreenshotRequestTimeoutError(error);
1150
- if (!Boolean(!nextOptions.miniProgram && (isProtocolTimeout || isNavigationTimeout || isScreenshotTimeout) && !hasRetriedWithFreshSession)) throw error;
1303
+ if (!Boolean(nextOptions.retryWithFreshSession !== false && !nextOptions.miniProgram && (isProtocolTimeout || isNavigationTimeout || isScreenshotTimeout) && !hasRetriedWithFreshSession)) throw error;
1151
1304
  hasRetriedWithFreshSession = true;
1152
1305
  await closeWechatIdeProject().catch((closeError) => {
1153
1306
  logger_default.warn(i18nText(`关闭当前微信开发者工具项目窗口失败:${closeError instanceof Error ? closeError.message : String(closeError)},仍将继续尝试重建自动化会话。`, `Failed to close the current Wechat DevTools project window: ${closeError instanceof Error ? closeError.message : String(closeError)}. Continuing to rebuild the automation session.`));
@@ -0,0 +1,2 @@
1
+ import { h as takeScreenshot } from "./commands-CK4N5Nel.js";
2
+ export { takeScreenshot };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { Buffer } from "node:buffer";
2
2
  import { AutomatorElement, AutomatorMiniProgram, AutomatorPage, DevtoolsConnectionInput, DevtoolsContext, DevtoolsElementSnapshot, DevtoolsPageSnapshot, DevtoolsRuntimeSessionOptions, DevtoolsToolResult, MiniProgramEventMap, closeSharedMiniProgram, getSharedMiniProgramSessionCount, readDevtoolsElementSnapshot as readElementSnapshot, releaseSharedMiniProgram, resolveDevtoolsProjectPath as resolveProjectPath, toDevtoolsSerializableValue as toSerializableValue } from "@weapp-vite/devtools-runtime";
3
3
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
-
5
4
  //#region src/cli/automator.d.ts
6
5
  interface AutomatorOptions {
7
6
  projectPath: string;
@@ -143,6 +142,7 @@ interface ScreenshotOptions extends AutomatorCommandOptions {
143
142
  outputPath?: string;
144
143
  page?: string;
145
144
  fullPage?: boolean;
145
+ retryWithFreshSession?: boolean;
146
146
  }
147
147
  interface ScreenshotResult {
148
148
  base64?: string;
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { $ as overwriteCustomConfig, A as isAutomatorProtocolTimeoutError, B as getConfig, D as formatAutomatorLoginError, E as connectOpenedAutomator, F as launchAutomator, G as isOperatingSystemSupported, H as resolveDevtoolsAutomationDefaults, I as resolveProjectAutomatorPort, K as operatingSystemName, L as bootstrapWechatDevtoolsSettings, M as isDevtoolsExtensionContextInvalidatedError, N as isDevtoolsHttpPortError, O as getAutomatorProtocolTimeoutMethod, P as isRetryableAutomatorLaunchError, Q as createLocaleConfig, R as detectWechatDevtoolsServicePort, S as waitForExclusiveKeypress, U as SupportedPlatformsMap, V as getConfiguredLocale, W as getDefaultCliPath, X as createAutoTrustProjectConfig, Y as createAutoBootstrapDevtoolsConfig, Z as createCustomConfig, _ as promptRetryKeypress, a as releaseSharedMiniProgram, b as createSharedInputSession, c as RETRY_CANCEL_KEYS, d as createWechatIdeLoginRequiredExitError, et as readCustomConfig, f as extractExecutionErrorText, g as isWechatIdeLoginRequiredExitError, h as isWechatIdeLoginRequiredError, i as getSharedMiniProgramSessionCount, it as resolvePath, j as isAutomatorWsConnectError, k as isAutomatorLoginError, l as RETRY_CONFIRM_KEYS, m as formatWechatIdeLoginRequiredError, n as closeSharedMiniProgram, nt as defaultCustomConfigDirPath, o as withMiniProgram, p as formatRetryHotkeyPrompt, r as connectMiniProgram, rt as defaultCustomConfigFilePath, s as runRetryableCommand, t as acquireSharedMiniProgram, tt as removeCustomConfigKey, u as RETRY_PROMPT_INITIAL_IGNORE_MS, v as promptWechatIdeLoginRetry, x as runWithSuspendedSharedInput, y as waitForRetryKeypress, z as resolveCliPath } from "./automator-session-C8JdS2rx.js";
2
- import { $ as getRuntimeWechatDevtoolsServicePort, A as isWechatIdeLoggedIn, C as clearWechatIdeCache, D as getWechatIdeTestAccounts, E as compileWechatIdeByAutomator, F as quitWechatIde, G as transformArgv, H as execute, I as refreshWechatIdeTicket, J as openWechatIdeProjectByHttp, K as promptForCliPath, L as resetWechatIdeFileUtils, M as openWechatIde, N as openWechatIdeOtherProject, O as getWechatIdeTicket, P as previewWechatIde, Q as startWechatIdeEngineBuildByHttp, R as setWechatIdeTicket, S as buildWechatIdeNpm, T as closeWechatIdeProject, U as createAlias, V as runWechatCliWithRetry, W as createPathCompat, X as requestWechatDevtoolsHttp, Y as pollWechatIdeEngineBuildResultByHttp, Z as resetWechatIdeFileUtilsByHttp, _ as autoPreviewWechatIde, a as navigateBack, b as buildWechatIdeApk, c as pageStack, d as remote, et as setRuntimeWechatDevtoolsServicePort, f as scrollTo, g as tap, h as takeScreenshot, i as input, it as removeOption, j as loginWechatIde, k as getWechatIdeToolInfo, l as reLaunch, m as systemInfo, n as captureScreenshotBuffer, nt as readBooleanOption, o as navigateTo, p as switchTab, q as WECHAT_DEVTOOLS_ENGINE_BUILD_STATUSES, r as currentPage, rt as readOptionValue, s as pageData, t as audit, tt as parseAutomatorArgs, u as redirectTo, v as autoReplayWechatIde, w as clearWechatIdeCacheByAutomator, x as buildWechatIdeIpa, y as autoWechatIde, z as uploadWechatIde } from "./commands-C6ATyJuz.js";
3
- import { C as parseCompareArgs, S as printScreenshotHelp, _ as AUTOMATOR_COMMAND_NAMES, a as runMinidev, b as runAutomatorCommand, c as runWechatIdeEngineBuild, d as CONFIG_COMMAND_NAME, f as MCP_COMMAND_NAME, g as isWeappIdeTopLevelCommand, h as WECHAT_CLI_COMMAND_NAMES, i as validateWechatCliCommandArgs, l as runWechatIdeEngineBuildByHttp, m as WEAPP_IDE_TOP_LEVEL_COMMAND_NAMES, n as parse, o as startForwardConsole, p as MINIDEV_NAMESPACE_COMMAND_NAMES, r as dispatchWechatCliCommand, s as isWechatIdeEngineBuildEndpointMissingError, t as createCli, u as handleConfigCommand, v as getAutomatorCommandHelp, w as printCompareHelp, x as parseScreenshotArgs, y as isAutomatorCommand } from "./cli-BUx9wWVK.js";
4
- import { a as readElementSnapshot, i as registerWeappIdeMcpTools, n as startWeappIdeMcpServer, o as resolveProjectPath, r as createWeappIdeMcpServer, s as toSerializableValue, t as runMcpCommand } from "./run-mcp-X00XjC8Z.js";
1
+ import { $ as overwriteCustomConfig, A as isAutomatorProtocolTimeoutError, B as getConfig, D as formatAutomatorLoginError, E as connectOpenedAutomator, F as launchAutomator, G as isOperatingSystemSupported, H as resolveDevtoolsAutomationDefaults, I as resolveProjectAutomatorPort, K as operatingSystemName, L as bootstrapWechatDevtoolsSettings, M as isDevtoolsExtensionContextInvalidatedError, N as isDevtoolsHttpPortError, O as getAutomatorProtocolTimeoutMethod, P as isRetryableAutomatorLaunchError, Q as createLocaleConfig, R as detectWechatDevtoolsServicePort, S as waitForExclusiveKeypress, U as SupportedPlatformsMap, V as getConfiguredLocale, W as getDefaultCliPath, X as createAutoTrustProjectConfig, Y as createAutoBootstrapDevtoolsConfig, Z as createCustomConfig, _ as promptRetryKeypress, a as releaseSharedMiniProgram, b as createSharedInputSession, c as RETRY_CANCEL_KEYS, d as createWechatIdeLoginRequiredExitError, et as readCustomConfig, f as extractExecutionErrorText, g as isWechatIdeLoginRequiredExitError, h as isWechatIdeLoginRequiredError, i as getSharedMiniProgramSessionCount, it as resolvePath, j as isAutomatorWsConnectError, k as isAutomatorLoginError, l as RETRY_CONFIRM_KEYS, m as formatWechatIdeLoginRequiredError, n as closeSharedMiniProgram, nt as defaultCustomConfigDirPath, o as withMiniProgram, p as formatRetryHotkeyPrompt, r as connectMiniProgram, rt as defaultCustomConfigFilePath, s as runRetryableCommand, t as acquireSharedMiniProgram, tt as removeCustomConfigKey, u as RETRY_PROMPT_INITIAL_IGNORE_MS, v as promptWechatIdeLoginRetry, x as runWithSuspendedSharedInput, y as waitForRetryKeypress, z as resolveCliPath } from "./automator-session-ec0i76Cx.js";
2
+ import { $ as getRuntimeWechatDevtoolsServicePort, A as isWechatIdeLoggedIn, C as clearWechatIdeCache, D as getWechatIdeTestAccounts, E as compileWechatIdeByAutomator, F as quitWechatIde, G as transformArgv, H as execute, I as refreshWechatIdeTicket, J as openWechatIdeProjectByHttp, K as promptForCliPath, L as resetWechatIdeFileUtils, M as openWechatIde, N as openWechatIdeOtherProject, O as getWechatIdeTicket, P as previewWechatIde, Q as startWechatIdeEngineBuildByHttp, R as setWechatIdeTicket, S as buildWechatIdeNpm, T as closeWechatIdeProject, U as createAlias, V as runWechatCliWithRetry, W as createPathCompat, X as requestWechatDevtoolsHttp, Y as pollWechatIdeEngineBuildResultByHttp, Z as resetWechatIdeFileUtilsByHttp, _ as autoPreviewWechatIde, a as navigateBack, b as buildWechatIdeApk, c as pageStack, d as remote, et as setRuntimeWechatDevtoolsServicePort, f as scrollTo, g as tap, h as takeScreenshot, i as input, it as removeOption, j as loginWechatIde, k as getWechatIdeToolInfo, l as reLaunch, m as systemInfo, n as captureScreenshotBuffer, nt as readBooleanOption, o as navigateTo, p as switchTab, q as WECHAT_DEVTOOLS_ENGINE_BUILD_STATUSES, r as currentPage, rt as readOptionValue, s as pageData, t as audit, tt as parseAutomatorArgs, u as redirectTo, v as autoReplayWechatIde, w as clearWechatIdeCacheByAutomator, x as buildWechatIdeIpa, y as autoWechatIde, z as uploadWechatIde } from "./commands-CK4N5Nel.js";
3
+ import { C as parseCompareArgs, S as printScreenshotHelp, _ as AUTOMATOR_COMMAND_NAMES, a as runMinidev, b as runAutomatorCommand, c as runWechatIdeEngineBuild, d as CONFIG_COMMAND_NAME, f as MCP_COMMAND_NAME, g as isWeappIdeTopLevelCommand, h as WECHAT_CLI_COMMAND_NAMES, i as validateWechatCliCommandArgs, l as runWechatIdeEngineBuildByHttp, m as WEAPP_IDE_TOP_LEVEL_COMMAND_NAMES, n as parse, o as startForwardConsole, p as MINIDEV_NAMESPACE_COMMAND_NAMES, r as dispatchWechatCliCommand, s as isWechatIdeEngineBuildEndpointMissingError, t as createCli, u as handleConfigCommand, v as getAutomatorCommandHelp, w as printCompareHelp, x as parseScreenshotArgs, y as isAutomatorCommand } from "./cli-BBXXj7vb.js";
4
+ import { a as readElementSnapshot, i as registerWeappIdeMcpTools, n as startWeappIdeMcpServer, o as resolveProjectPath, r as createWeappIdeMcpServer, s as toSerializableValue, t as runMcpCommand } from "./run-mcp-DPz-bwrD.js";
5
5
  export { AUTOMATOR_COMMAND_NAMES, CONFIG_COMMAND_NAME, MCP_COMMAND_NAME, MINIDEV_NAMESPACE_COMMAND_NAMES, RETRY_CANCEL_KEYS, RETRY_CONFIRM_KEYS, RETRY_PROMPT_INITIAL_IGNORE_MS, SupportedPlatformsMap, WEAPP_IDE_TOP_LEVEL_COMMAND_NAMES, WECHAT_CLI_COMMAND_NAMES, WECHAT_DEVTOOLS_ENGINE_BUILD_STATUSES, acquireSharedMiniProgram, audit, autoPreviewWechatIde, autoReplayWechatIde, autoWechatIde, bootstrapWechatDevtoolsSettings, buildWechatIdeApk, buildWechatIdeIpa, buildWechatIdeNpm, captureScreenshotBuffer, clearWechatIdeCache, clearWechatIdeCacheByAutomator, closeSharedMiniProgram, closeWechatIdeProject, compileWechatIdeByAutomator, connectMiniProgram, connectOpenedAutomator, createAlias, createAutoBootstrapDevtoolsConfig, createAutoTrustProjectConfig, createCli, createCustomConfig, createLocaleConfig, createPathCompat, createSharedInputSession, createWeappIdeMcpServer, createWechatIdeLoginRequiredExitError, currentPage, defaultCustomConfigDirPath, defaultCustomConfigFilePath, detectWechatDevtoolsServicePort, dispatchWechatCliCommand, execute, extractExecutionErrorText, formatAutomatorLoginError, formatRetryHotkeyPrompt, formatWechatIdeLoginRequiredError, getAutomatorCommandHelp, getAutomatorProtocolTimeoutMethod, getConfig, getConfiguredLocale, getDefaultCliPath, getRuntimeWechatDevtoolsServicePort, getSharedMiniProgramSessionCount, getWechatIdeTestAccounts, getWechatIdeTicket, getWechatIdeToolInfo, handleConfigCommand, input, isAutomatorCommand, isAutomatorLoginError, isAutomatorProtocolTimeoutError, isAutomatorWsConnectError, isDevtoolsExtensionContextInvalidatedError, isDevtoolsHttpPortError, isOperatingSystemSupported, isRetryableAutomatorLaunchError, isWeappIdeTopLevelCommand, isWechatIdeEngineBuildEndpointMissingError, isWechatIdeLoggedIn, isWechatIdeLoginRequiredError, isWechatIdeLoginRequiredExitError, launchAutomator, loginWechatIde, navigateBack, navigateTo, openWechatIde, openWechatIdeOtherProject, openWechatIdeProjectByHttp, operatingSystemName, overwriteCustomConfig, pageData, pageStack, parse, parseAutomatorArgs, parseCompareArgs, parseScreenshotArgs, pollWechatIdeEngineBuildResultByHttp, previewWechatIde, printCompareHelp, printScreenshotHelp, promptForCliPath, promptRetryKeypress, promptWechatIdeLoginRetry, quitWechatIde, reLaunch, readBooleanOption, readCustomConfig, readElementSnapshot, readOptionValue, redirectTo, refreshWechatIdeTicket, registerWeappIdeMcpTools, releaseSharedMiniProgram, remote, removeCustomConfigKey, removeOption, requestWechatDevtoolsHttp, resetWechatIdeFileUtils, resetWechatIdeFileUtilsByHttp, resolveCliPath, resolveDevtoolsAutomationDefaults, resolvePath, resolveProjectAutomatorPort, resolveProjectPath, runAutomatorCommand, runMcpCommand, runMinidev, runRetryableCommand, runWechatCliWithRetry, runWechatIdeEngineBuild, runWechatIdeEngineBuildByHttp, runWithSuspendedSharedInput, scrollTo, setRuntimeWechatDevtoolsServicePort, setWechatIdeTicket, startForwardConsole, startWeappIdeMcpServer, startWechatIdeEngineBuildByHttp, switchTab, systemInfo, takeScreenshot, tap, toSerializableValue, transformArgv, uploadWechatIde, validateWechatCliCommandArgs, waitForExclusiveKeypress, waitForRetryKeypress, withMiniProgram };
@@ -0,0 +1,2 @@
1
+ import { t as runMcpCommand } from "./run-mcp-DPz-bwrD.js";
2
+ export { runMcpCommand };
@@ -1,4 +1,4 @@
1
- import { o as withMiniProgram$1 } from "./automator-session-C8JdS2rx.js";
1
+ import { o as withMiniProgram$1 } from "./automator-session-ec0i76Cx.js";
2
2
  import { Buffer } from "node:buffer";
3
3
  import fs from "node:fs/promises";
4
4
  import path from "node:path";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "weapp-ide-cli",
3
3
  "type": "module",
4
- "version": "5.4.9",
4
+ "version": "5.4.11",
5
5
  "description": "让微信开发者工具,用起来更加方便!",
6
6
  "author": "ice breaker <1324318532@qq.com>",
7
7
  "license": "MIT",
@@ -71,8 +71,8 @@
71
71
  "zod": "^4.4.3",
72
72
  "@weapp-core/logger": "3.1.1",
73
73
  "@weapp-core/shared": "3.0.5",
74
- "@weapp-vite/devtools-runtime": "0.4.6",
75
- "@weapp-vite/miniprogram-automator": "1.2.5"
74
+ "@weapp-vite/miniprogram-automator": "1.2.6",
75
+ "@weapp-vite/devtools-runtime": "0.4.7"
76
76
  },
77
77
  "scripts": {
78
78
  "dev": "tsdown -w --sourcemap",
@@ -1,2 +0,0 @@
1
- import { h as takeScreenshot } from "./commands-C6ATyJuz.js";
2
- export { takeScreenshot };
@@ -1,2 +0,0 @@
1
- import { t as runMcpCommand } from "./run-mcp-X00XjC8Z.js";
2
- export { runMcpCommand };