@dimina-kit/devtools 0.4.0-dev.20260702182435 → 0.4.0-dev.20260703051110

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.
Files changed (50) hide show
  1. package/dist/main/app/app.js +5 -25
  2. package/dist/main/app/native-overview.d.ts +27 -0
  3. package/dist/main/app/native-overview.js +44 -0
  4. package/dist/main/index.bundle.js +826 -690
  5. package/dist/main/ipc/bridge-router.js +180 -141
  6. package/dist/main/ipc/project-fs.js +39 -21
  7. package/dist/main/services/automation/handlers/app.js +143 -109
  8. package/dist/main/services/mcp/tools/simulator-tools.js +118 -97
  9. package/dist/main/services/network-forward/dispatch-batch.d.ts +22 -0
  10. package/dist/main/services/network-forward/dispatch-batch.js +21 -0
  11. package/dist/main/services/network-forward/index.js +5 -20
  12. package/dist/main/services/projects/create-project-service.js +54 -34
  13. package/dist/main/services/projects/types.d.ts +1 -32
  14. package/dist/main/services/service-console/console-api.js +36 -27
  15. package/dist/main/services/simulator-temp-files/disk.js +109 -86
  16. package/dist/main/services/workbench-coi-server.js +58 -45
  17. package/dist/main/utils/logger.d.ts +5 -12
  18. package/dist/main/utils/logger.js +5 -45
  19. package/dist/preload/instrumentation/wxml-extract.js +94 -62
  20. package/dist/preload/runtime/main-api-runner.d.ts +1 -1
  21. package/dist/preload/windows/main.cjs +284 -565
  22. package/dist/preload/windows/main.cjs.map +2 -2
  23. package/dist/preload/windows/simulator.cjs +37 -39
  24. package/dist/preload/windows/simulator.cjs.map +2 -2
  25. package/dist/preload/windows/simulator.js +37 -39
  26. package/dist/render-host/render-inspect.js +63 -52
  27. package/dist/renderer/assets/index-D-ksyN1p.js +49 -0
  28. package/dist/renderer/assets/workbenchSettings-COhuFF-i.js +8 -0
  29. package/dist/renderer/entries/main/index.html +1 -1
  30. package/dist/renderer/entries/workbench-settings/index.html +1 -1
  31. package/dist/shared/appdata-accumulator.js +53 -48
  32. package/dist/shared/open-in-editor-resource-path.d.ts +56 -0
  33. package/dist/shared/open-in-editor-resource-path.js +133 -0
  34. package/dist/shared/open-in-editor.d.ts +2 -9
  35. package/dist/shared/open-in-editor.js +8 -93
  36. package/dist/shared/types.d.ts +13 -0
  37. package/dist/simulator/assets/{device-shell-CUl0ILfn.js → device-shell-CiLAPa0I.js} +2 -2
  38. package/dist/simulator/assets/simulator-Dvnxry3y.js +10 -0
  39. package/dist/simulator/simulator.html +1 -1
  40. package/dist/vscode-workbench/assets/__vite-browser-external-CXi6Kz9W.js +1 -0
  41. package/dist/vscode-workbench/assets/{dist-CS7SQP3K.js → dist-TpGpmMGi.js} +3 -3
  42. package/dist/vscode-workbench/assets/{iconv-lite-umd-D3q-1-o6.js → iconv-lite-umd-C9tiCAJa.js} +1 -1
  43. package/dist/vscode-workbench/assets/{index-zjigpZ25.js → index-DJ1HyMZ7.js} +20 -22
  44. package/dist/vscode-workbench/assets/{jschardet-Cu4ufeHq.js → jschardet-DE1jV513.js} +1 -1
  45. package/dist/vscode-workbench/index.html +1 -1
  46. package/package.json +5 -5
  47. package/dist/renderer/assets/index-B-Dqb7G0.js +0 -49
  48. package/dist/renderer/assets/workbenchSettings-Bim9ol9R.js +0 -8
  49. package/dist/simulator/assets/simulator-Dz8iGcgy.js +0 -10
  50. package/dist/vscode-workbench/assets/__vite-browser-external-B0DTNerG.js +0 -1
@@ -173,55 +173,20 @@ function validate(channel, schema, args) {
173
173
  }
174
174
 
175
175
  // src/main/utils/logger.ts
176
- var LEVEL_PRIORITY = {
177
- debug: 0,
178
- info: 1,
179
- warn: 2,
180
- error: 3
181
- };
182
- var LEVEL_PREFIX = {
183
- debug: "[DEBUG]",
184
- info: "[INFO]",
185
- warn: "[WARN]",
186
- error: "[ERROR]"
187
- };
188
- var currentLevel = "debug";
189
- function shouldLog(level) {
190
- return LEVEL_PRIORITY[level] >= LEVEL_PRIORITY[currentLevel];
191
- }
192
- function formatMessage(level, tag, message) {
193
- const ts = (/* @__PURE__ */ new Date()).toISOString();
194
- return `${ts} ${LEVEL_PREFIX[level]} [${tag}] ${message}`;
195
- }
196
- function createLogger(tag) {
197
- return {
198
- debug(message, ...args) {
199
- if (shouldLog("debug")) console.debug(formatMessage("debug", tag, message), ...args);
200
- },
201
- info(message, ...args) {
202
- if (shouldLog("info")) console.info(formatMessage("info", tag, message), ...args);
203
- },
204
- warn(message, ...args) {
205
- if (shouldLog("warn")) console.warn(formatMessage("warn", tag, message), ...args);
206
- },
207
- error(message, ...args) {
208
- if (shouldLog("error")) console.error(formatMessage("error", tag, message), ...args);
209
- }
210
- };
211
- }
176
+ import { createLogger, setLogLevel } from "@dimina-kit/electron-deck/main";
212
177
 
213
178
  // src/main/utils/ipc-registry.ts
214
179
  var log = createLogger("ipc");
215
- function reportListenerError(channel, err) {
216
- if (err instanceof IpcValidationError) {
217
- log.warn(`schema reject on '${channel}' at [${err.paths.join(", ")}]`);
180
+ function reportListenerError(channel, err2) {
181
+ if (err2 instanceof IpcValidationError) {
182
+ log.warn(`schema reject on '${channel}' at [${err2.paths.join(", ")}]`);
218
183
  return;
219
184
  }
220
- if (err instanceof Error) {
221
- log.error(`listener threw on '${channel}': ${err.message}`, err.stack);
185
+ if (err2 instanceof Error) {
186
+ log.error(`listener threw on '${channel}': ${err2.message}`, err2.stack);
222
187
  return;
223
188
  }
224
- log.error(`listener threw on '${channel}'`, err);
189
+ log.error(`listener threw on '${channel}'`, err2);
225
190
  }
226
191
  function isThenable(v) {
227
192
  return v != null && (typeof v === "object" || typeof v === "function") && typeof v.then === "function";
@@ -284,13 +249,13 @@ var IpcRegistry = class {
284
249
  return;
285
250
  }
286
251
  event.returnValue = fn(event, ...args);
287
- } catch (err) {
288
- reportListenerError(channel, err);
289
- const code = err?.code;
252
+ } catch (err2) {
253
+ reportListenerError(channel, err2);
254
+ const code = err2?.code;
290
255
  event.returnValue = {
291
256
  ok: false,
292
257
  code: typeof code === "string" ? code : "EUNKNOWN",
293
- message: err instanceof Error ? err.message : String(err)
258
+ message: err2 instanceof Error ? err2.message : String(err2)
294
259
  };
295
260
  }
296
261
  };
@@ -307,10 +272,10 @@ var IpcRegistry = class {
307
272
  try {
308
273
  const ret = raw(event, ...args);
309
274
  if (isThenable(ret)) {
310
- Promise.resolve(ret).catch((err) => reportListenerError(channel, err));
275
+ Promise.resolve(ret).catch((err2) => reportListenerError(channel, err2));
311
276
  }
312
- } catch (err) {
313
- reportListenerError(channel, err);
277
+ } catch (err2) {
278
+ reportListenerError(channel, err2);
314
279
  }
315
280
  };
316
281
  const guarded = policy ? (event, ...args) => {
@@ -555,9 +520,9 @@ var ReadFileSchema = z.tuple([PathArg]);
555
520
  var WriteFileSchema = z.tuple([PathArg, z.string()]);
556
521
  var ListFilesSchema = z.tuple([PathArg]);
557
522
  function makeError(code, message) {
558
- const err = new Error(message);
559
- err.code = code;
560
- return err;
523
+ const err2 = new Error(message);
524
+ err2.code = code;
525
+ return err2;
561
526
  }
562
527
  function isWithin(parent, child) {
563
528
  if (child === parent) return true;
@@ -600,8 +565,8 @@ async function resolveWithinProjectRoot(abs, root) {
600
565
  let realTarget;
601
566
  try {
602
567
  realTarget = await fs2.realpath(lexical);
603
- } catch (err) {
604
- if (err.code !== "ENOENT") throw err;
568
+ } catch (err2) {
569
+ if (err2.code !== "ENOENT") throw err2;
605
570
  const parent = path2.dirname(lexical);
606
571
  const realParent = await fs2.realpath(parent);
607
572
  if (!isWithin(realRoot, realParent)) {
@@ -648,8 +613,8 @@ async function assertWritableAncestor(lexical, root) {
648
613
  let realAncestor;
649
614
  try {
650
615
  realAncestor = await fs2.realpath(ancestor);
651
- } catch (err) {
652
- if (err.code !== "ENOENT") throw err;
616
+ } catch (err2) {
617
+ if (err2.code !== "ENOENT") throw err2;
653
618
  const next = path2.dirname(ancestor);
654
619
  if (next === ancestor) {
655
620
  throw makeError("EACCES", `Path escapes project root: ${lexical}`);
@@ -714,9 +679,9 @@ async function deleteWithin(root, rel) {
714
679
  let safe;
715
680
  try {
716
681
  safe = await resolveWithinProjectRoot(joinRel(root, rel), root);
717
- } catch (err) {
718
- if (err.code === "ENOENT") return;
719
- throw err;
682
+ } catch (err2) {
683
+ if (err2.code === "ENOENT") return;
684
+ throw err2;
720
685
  }
721
686
  await fs2.rm(safe, { recursive: true, force: true });
722
687
  }
@@ -733,8 +698,8 @@ function resolveWithinProjectRootSync(abs, root) {
733
698
  let realTarget;
734
699
  try {
735
700
  realTarget = nodeFs.realpathSync(lexical);
736
- } catch (err) {
737
- if (err.code !== "ENOENT") throw err;
701
+ } catch (err2) {
702
+ if (err2.code !== "ENOENT") throw err2;
738
703
  const parent = path2.dirname(lexical);
739
704
  const realParent = nodeFs.realpathSync(parent);
740
705
  if (!isWithin(realRoot, realParent)) {
@@ -754,8 +719,8 @@ function assertWritableAncestorSync(lexical, root) {
754
719
  let realAncestor;
755
720
  try {
756
721
  realAncestor = nodeFs.realpathSync(ancestor);
757
- } catch (err) {
758
- if (err.code !== "ENOENT") throw err;
722
+ } catch (err2) {
723
+ if (err2.code !== "ENOENT") throw err2;
759
724
  const next = path2.dirname(ancestor);
760
725
  if (next === ancestor) {
761
726
  throw makeError("EACCES", `Path escapes project root: ${lexical}`);
@@ -800,6 +765,25 @@ function writeFileSync(root, p, content) {
800
765
  nodeFs.closeSync(fd);
801
766
  }
802
767
  }
768
+ async function readDirEntriesSafe(dir) {
769
+ try {
770
+ return await fs2.readdir(dir, { withFileTypes: true });
771
+ } catch (err2) {
772
+ const code = err2.code;
773
+ if (code === "EACCES" || code === "ENOENT" || code === "ENOTDIR") return null;
774
+ throw err2;
775
+ }
776
+ }
777
+ function collectListEntry(entry, dir, safeRoot, out, queue) {
778
+ const full = path2.join(dir, entry.name);
779
+ if (entry.isDirectory()) {
780
+ if (!SKIP_DIRS.has(entry.name)) queue.push(full);
781
+ return;
782
+ }
783
+ if (entry.isFile()) {
784
+ out.push(path2.relative(safeRoot, full).split(path2.sep).join("/"));
785
+ }
786
+ }
803
787
  async function listFiles(root, rootArg) {
804
788
  const safeRoot = await resolveWithinProjectRoot(rootArg, root);
805
789
  const out = [];
@@ -807,24 +791,11 @@ async function listFiles(root, rootArg) {
807
791
  while (queue.length > 0) {
808
792
  if (out.length >= LIST_FILE_LIMIT) break;
809
793
  const dir = queue.shift();
810
- let entries;
811
- try {
812
- entries = await fs2.readdir(dir, { withFileTypes: true });
813
- } catch (err) {
814
- const code = err.code;
815
- if (code === "EACCES" || code === "ENOENT" || code === "ENOTDIR") continue;
816
- throw err;
817
- }
818
- for (const e of entries) {
794
+ const entries = await readDirEntriesSafe(dir);
795
+ if (entries === null) continue;
796
+ for (const entry of entries) {
819
797
  if (out.length >= LIST_FILE_LIMIT) break;
820
- const full = path2.join(dir, e.name);
821
- if (e.isDirectory()) {
822
- if (SKIP_DIRS.has(e.name)) continue;
823
- queue.push(full);
824
- continue;
825
- }
826
- if (!e.isFile()) continue;
827
- out.push(path2.relative(safeRoot, full).split(path2.sep).join("/"));
798
+ collectListEntry(entry, dir, safeRoot, out, queue);
828
799
  }
829
800
  }
830
801
  return out;
@@ -1050,6 +1021,37 @@ function registerAppLifecycle() {
1050
1021
  });
1051
1022
  }
1052
1023
 
1024
+ // src/main/app/native-overview.ts
1025
+ async function resolveNativeStorageOverview(context, appId) {
1026
+ try {
1027
+ const info = await context.storageApi?.invoke(appId, "getStorageInfo", {});
1028
+ if (info && typeof info === "object") {
1029
+ const keys = info.keys;
1030
+ if (Array.isArray(keys)) {
1031
+ return {
1032
+ storageKeys: keys.filter((key) => typeof key === "string"),
1033
+ storageCount: keys.length
1034
+ };
1035
+ }
1036
+ }
1037
+ } catch {
1038
+ }
1039
+ return { storageKeys: [], storageCount: 0 };
1040
+ }
1041
+ function resolveNativeAppDataKeys(context, appId) {
1042
+ try {
1043
+ const snapshot = context.appData?.snapshot?.(appId);
1044
+ if (snapshot && typeof snapshot === "object") {
1045
+ const entries = snapshot.entries;
1046
+ if (entries && typeof entries === "object") {
1047
+ return Object.keys(entries);
1048
+ }
1049
+ }
1050
+ } catch {
1051
+ }
1052
+ return [];
1053
+ }
1054
+
1053
1055
  // src/main/services/workbench-context.ts
1054
1056
  import {
1055
1057
  createConnectionRegistry,
@@ -1150,8 +1152,8 @@ function createSafeAreaController(options = {}) {
1150
1152
  const attached = /* @__PURE__ */ new Map();
1151
1153
  function override(wc, device, isTabPage2) {
1152
1154
  if (wc.isDestroyed()) return;
1153
- void wc.debugger.sendCommand("Emulation.setSafeAreaInsetsOverride", { insets: guestInsets(device, isTabPage2) }).catch((err) => {
1154
- console.warn("[safe-area] setSafeAreaInsetsOverride failed:", err instanceof Error ? err.message : err);
1155
+ void wc.debugger.sendCommand("Emulation.setSafeAreaInsetsOverride", { insets: guestInsets(device, isTabPage2) }).catch((err2) => {
1156
+ console.warn("[safe-area] setSafeAreaInsetsOverride failed:", err2 instanceof Error ? err2.message : err2);
1155
1157
  });
1156
1158
  }
1157
1159
  return {
@@ -1162,8 +1164,8 @@ function createSafeAreaController(options = {}) {
1162
1164
  }
1163
1165
  try {
1164
1166
  wc.debugger.attach("1.3");
1165
- } catch (err) {
1166
- console.warn("[safe-area] debugger.attach failed; env(safe-area-inset-*) stays 0:", err instanceof Error ? err.message : err);
1167
+ } catch (err2) {
1168
+ console.warn("[safe-area] debugger.attach failed; env(safe-area-inset-*) stays 0:", err2 instanceof Error ? err2.message : err2);
1167
1169
  return;
1168
1170
  }
1169
1171
  attached.set(wc, isTabPage2);
@@ -1403,8 +1405,8 @@ function createWorkbenchView(ctx, reconciler) {
1403
1405
  }
1404
1406
  ctx.windows.mainWindow.contentView.addChildView(view);
1405
1407
  const loadUrl = `${url}index.html?theme=${workbenchThemeScheme()}`;
1406
- await view.webContents.loadURL(loadUrl).catch((err) => {
1407
- console.error("[workbench] attachWorkbench \u2014 loadURL failed", err);
1408
+ await view.webContents.loadURL(loadUrl).catch((err2) => {
1409
+ console.error("[workbench] attachWorkbench \u2014 loadURL failed", err2);
1408
1410
  });
1409
1411
  }
1410
1412
  function setWorkbenchSource(url) {
@@ -1439,7 +1441,7 @@ function createWorkbenchView(ctx, reconciler) {
1439
1441
  return true
1440
1442
  } catch (e) { return false }
1441
1443
  })()`;
1442
- return workbenchView.webContents.executeJavaScript(script, true).then((ok) => ok === true).catch(() => false);
1444
+ return workbenchView.webContents.executeJavaScript(script, true).then((ok2) => ok2 === true).catch(() => false);
1443
1445
  }
1444
1446
  function openFileInWorkbench(relPath, line, column) {
1445
1447
  if (!workbenchView || workbenchView.webContents.isDestroyed()) return false;
@@ -1476,6 +1478,84 @@ function createWorkbenchView(ctx, reconciler) {
1476
1478
  // src/main/services/views/native-simulator-devtools-host.ts
1477
1479
  import { WebContentsView as WebContentsView3 } from "electron";
1478
1480
 
1481
+ // src/shared/open-in-editor-resource-path.ts
1482
+ function decodePathname(pathname) {
1483
+ return pathname.split("/").map((segment) => {
1484
+ try {
1485
+ return decodeURIComponent(segment);
1486
+ } catch {
1487
+ return segment;
1488
+ }
1489
+ }).join("/");
1490
+ }
1491
+ function normalizeSlashPath(value) {
1492
+ return decodePathname(value.replace(/\\/g, "/")).replace(/\/+/g, "/");
1493
+ }
1494
+ function safeRelativePath(segments) {
1495
+ if (segments.length === 0 || segments.some((segment) => !segment || segment === "." || segment === "..")) {
1496
+ return null;
1497
+ }
1498
+ return segments.join("/");
1499
+ }
1500
+ function excludeBuildChunk(rel) {
1501
+ if (!rel) return rel;
1502
+ const baseName = rel.split("/").pop();
1503
+ return baseName === "common.js" || baseName === "vendors.js" || baseName === "runtime.js" || baseName === "taro.js" || baseName === "babelHelpers.js" ? null : rel;
1504
+ }
1505
+ function isOutsideKnownRoot(normalizedRaw) {
1506
+ return /^\/(?:Volumes|Users|home|private|tmp|var|opt|Applications)\//.test(normalizedRaw);
1507
+ }
1508
+ function resolveFilesystemRawPath(normalizedRaw, projectRoot) {
1509
+ if (!projectRoot) return void 0;
1510
+ if (normalizedRaw !== projectRoot && !normalizedRaw.startsWith(`${projectRoot}/`)) return void 0;
1511
+ const relative = normalizedRaw.slice(projectRoot.length).replace(/^\/+/, "");
1512
+ return excludeBuildChunk(safeRelativePath(relative.split("/").filter(Boolean)));
1513
+ }
1514
+ function parseResourceUrl(raw, base) {
1515
+ try {
1516
+ return raw.startsWith("/") && !raw.startsWith("//") ? new URL(raw, base.origin) : new URL(raw);
1517
+ } catch {
1518
+ return null;
1519
+ }
1520
+ }
1521
+ function resolveFileProtocolPath(resource, projectRoot) {
1522
+ const filePath = normalizeSlashPath(resource.pathname);
1523
+ if (!projectRoot || filePath !== projectRoot && !filePath.startsWith(`${projectRoot}/`)) return null;
1524
+ return excludeBuildChunk(safeRelativePath(
1525
+ filePath.slice(projectRoot.length).replace(/^\/+/, "").split("/").filter(Boolean)
1526
+ ));
1527
+ }
1528
+ function resolveHttpProtocolPath(resource, base, context) {
1529
+ if (resource.protocol !== "http:" && resource.protocol !== "https:") return null;
1530
+ if (resource.origin !== base.origin) return null;
1531
+ let segments = decodePathname(resource.pathname).split("/").filter(Boolean);
1532
+ if (context.appId && segments[0] === context.appId) {
1533
+ segments = segments.slice(1);
1534
+ if (context.outputRoot && segments[0] === context.outputRoot) segments = segments.slice(1);
1535
+ }
1536
+ return excludeBuildChunk(safeRelativePath(segments));
1537
+ }
1538
+ function projectAwareResourcePath(resourceUrl, context) {
1539
+ let base;
1540
+ try {
1541
+ base = new URL(context.resourceBaseUrl);
1542
+ } catch {
1543
+ return null;
1544
+ }
1545
+ const projectRoot = normalizeSlashPath(context.projectRoot).replace(/\/$/, "");
1546
+ const raw = resourceUrl.trim();
1547
+ if (!raw) return null;
1548
+ const normalizedRaw = normalizeSlashPath(raw);
1549
+ const filesystemRelative = resolveFilesystemRawPath(normalizedRaw, projectRoot);
1550
+ if (filesystemRelative !== void 0) return filesystemRelative;
1551
+ if (isOutsideKnownRoot(normalizedRaw)) return null;
1552
+ if (!raw.startsWith("/") && !/^[a-z][a-z0-9+.-]*:/i.test(raw)) return null;
1553
+ const resource = parseResourceUrl(raw, base);
1554
+ if (!resource) return null;
1555
+ if (resource.protocol === "file:") return resolveFileProtocolPath(resource, projectRoot);
1556
+ return resolveHttpProtocolPath(resource, base, context);
1557
+ }
1558
+
1479
1559
  // src/shared/open-in-editor.ts
1480
1560
  var OPEN_IN_EDITOR_SCHEME = "dimina-open-in-editor";
1481
1561
  function decodeOpenInEditorUrl(raw) {
@@ -1531,70 +1611,6 @@ function resourceUrlToProjectRelativePath(resourceUrl, expectedOriginOrContext)
1531
1611
  if (segments.length < 2) return null;
1532
1612
  return segments.slice(1).join("/");
1533
1613
  }
1534
- function decodePathname(pathname) {
1535
- return pathname.split("/").map((segment) => {
1536
- try {
1537
- return decodeURIComponent(segment);
1538
- } catch {
1539
- return segment;
1540
- }
1541
- }).join("/");
1542
- }
1543
- function normalizeSlashPath(value) {
1544
- return decodePathname(value.replace(/\\/g, "/")).replace(/\/+/g, "/");
1545
- }
1546
- function safeRelativePath(segments) {
1547
- if (segments.length === 0 || segments.some((segment) => !segment || segment === "." || segment === "..")) {
1548
- return null;
1549
- }
1550
- return segments.join("/");
1551
- }
1552
- function projectAwareResourcePath(resourceUrl, context) {
1553
- let base;
1554
- try {
1555
- base = new URL(context.resourceBaseUrl);
1556
- } catch {
1557
- return null;
1558
- }
1559
- const projectRoot = normalizeSlashPath(context.projectRoot).replace(/\/$/, "");
1560
- const raw = resourceUrl.trim();
1561
- if (!raw) return null;
1562
- const excludeBuildChunk = (rel) => {
1563
- if (!rel) return rel;
1564
- const base2 = rel.split("/").pop();
1565
- return base2 === "common.js" || base2 === "vendors.js" || base2 === "runtime.js" || base2 === "taro.js" || base2 === "babelHelpers.js" ? null : rel;
1566
- };
1567
- const normalizedRaw = normalizeSlashPath(raw);
1568
- if (projectRoot && (normalizedRaw === projectRoot || normalizedRaw.startsWith(`${projectRoot}/`))) {
1569
- const relative = normalizedRaw.slice(projectRoot.length).replace(/^\/+/, "");
1570
- return excludeBuildChunk(safeRelativePath(relative.split("/").filter(Boolean)));
1571
- }
1572
- if (/^\/(?:Volumes|Users|home|private|tmp|var|opt|Applications)\//.test(normalizedRaw)) {
1573
- return null;
1574
- }
1575
- if (!raw.startsWith("/") && !/^[a-z][a-z0-9+.-]*:/i.test(raw)) return null;
1576
- let resource;
1577
- try {
1578
- resource = raw.startsWith("/") && !raw.startsWith("//") ? new URL(raw, base.origin) : new URL(raw);
1579
- } catch {
1580
- return null;
1581
- }
1582
- if (resource.protocol === "file:") {
1583
- const filePath = normalizeSlashPath(resource.pathname);
1584
- if (!projectRoot || filePath !== projectRoot && !filePath.startsWith(`${projectRoot}/`)) return null;
1585
- return excludeBuildChunk(safeRelativePath(
1586
- filePath.slice(projectRoot.length).replace(/^\/+/, "").split("/").filter(Boolean)
1587
- ));
1588
- }
1589
- if (resource.protocol !== "http:" && resource.protocol !== "https:") return null;
1590
- if (resource.origin !== base.origin) return null;
1591
- let segments = decodePathname(resource.pathname).split("/").filter(Boolean);
1592
- if (context.appId && segments[0] === context.appId) {
1593
- segments = segments.slice(1);
1594
- if (context.outputRoot && segments[0] === context.outputRoot) segments = segments.slice(1);
1595
- }
1596
- return excludeBuildChunk(safeRelativePath(segments));
1597
- }
1598
1614
  function projectSourceContextFromServiceHostUrl(serviceHostUrl, activeProjectRoot) {
1599
1615
  let url;
1600
1616
  try {
@@ -1632,10 +1648,16 @@ function buildDevtoolsProjectSourceLinksScript(context) {
1632
1648
  }
1633
1649
 
1634
1650
  const cfg = ${config}
1635
- const diminaProjectSourcePath = ${projectAwareResourcePath.toString()}
1636
1651
  const decodePathname = ${decodePathname.toString()}
1637
1652
  const normalizeSlashPath = ${normalizeSlashPath.toString()}
1638
1653
  const safeRelativePath = ${safeRelativePath.toString()}
1654
+ const excludeBuildChunk = ${excludeBuildChunk.toString()}
1655
+ const isOutsideKnownRoot = ${isOutsideKnownRoot.toString()}
1656
+ const resolveFilesystemRawPath = ${resolveFilesystemRawPath.toString()}
1657
+ const parseResourceUrl = ${parseResourceUrl.toString()}
1658
+ const resolveFileProtocolPath = ${resolveFileProtocolPath.toString()}
1659
+ const resolveHttpProtocolPath = ${resolveHttpProtocolPath.toString()}
1660
+ const diminaProjectSourcePath = ${projectAwareResourcePath.toString()}
1639
1661
  const observedRoots = new Set()
1640
1662
  const observers = []
1641
1663
  const clickBindings = []
@@ -2243,9 +2265,9 @@ function installElementsForward(deps) {
2243
2265
  });
2244
2266
  }
2245
2267
  replyResult(cmd, result);
2246
- }).catch((err) => {
2268
+ }).catch((err2) => {
2247
2269
  if (disposed) return;
2248
- replyError(cmd, err instanceof Error ? err.message : String(err));
2270
+ replyError(cmd, err2 instanceof Error ? err2.message : String(err2));
2249
2271
  });
2250
2272
  }
2251
2273
  function handleOutbound(batch) {
@@ -2325,18 +2347,15 @@ function mapConsoleApiType(type) {
2325
2347
  return "log";
2326
2348
  }
2327
2349
  var BIGINT_LITERAL = /^-?\d+n$/;
2328
- function remoteObjectToValue(ro) {
2329
- if (!ro) return ro;
2330
- if ("value" in ro) return ro.value;
2331
- if (typeof ro.unserializableValue === "string") {
2332
- const u = ro.unserializableValue;
2333
- if (u === "Infinity") return Infinity;
2334
- if (u === "-Infinity") return -Infinity;
2335
- if (u === "NaN") return NaN;
2336
- if (u === "-0") return -0;
2337
- if (BIGINT_LITERAL.test(u)) return u;
2338
- return u;
2339
- }
2350
+ function unserializableToValue(u) {
2351
+ if (u === "Infinity") return Infinity;
2352
+ if (u === "-Infinity") return -Infinity;
2353
+ if (u === "NaN") return NaN;
2354
+ if (u === "-0") return -0;
2355
+ if (BIGINT_LITERAL.test(u)) return u;
2356
+ return u;
2357
+ }
2358
+ function typeFallbackToValue(ro) {
2340
2359
  switch (ro.type) {
2341
2360
  case "undefined":
2342
2361
  return void 0;
@@ -2351,6 +2370,14 @@ function remoteObjectToValue(ro) {
2351
2370
  return ro.description ?? "[Unknown]";
2352
2371
  }
2353
2372
  }
2373
+ function remoteObjectToValue(ro) {
2374
+ if (!ro) return ro;
2375
+ if ("value" in ro) return ro.value;
2376
+ if (typeof ro.unserializableValue === "string") {
2377
+ return unserializableToValue(ro.unserializableValue);
2378
+ }
2379
+ return typeFallbackToValue(ro);
2380
+ }
2354
2381
  function needsDeepFetch(ro) {
2355
2382
  if (!ro) return false;
2356
2383
  if ("value" in ro) return false;
@@ -2627,8 +2654,8 @@ function createDevtoolsHost(ctx, reconciler, deps) {
2627
2654
  emit: (entry) => ctx.consoleForwarder?.emit(entry)
2628
2655
  }).stop;
2629
2656
  return true;
2630
- } catch (err) {
2631
- console.warn("[devtools-host] point-at-service-wc failed; retry will rebuild:", err);
2657
+ } catch (err2) {
2658
+ console.warn("[devtools-host] point-at-service-wc failed; retry will rebuild:", err2);
2632
2659
  if (nativeDevtoolsSourceWc?.id === next.id) {
2633
2660
  nativeDevtoolsSourceWc = null;
2634
2661
  }
@@ -2822,8 +2849,8 @@ function createHostToolbarPortChannel(opts) {
2822
2849
  function invokeReadyHandler(handler) {
2823
2850
  try {
2824
2851
  handler();
2825
- } catch (err) {
2826
- console.error("[host-toolbar] onReady handler threw:", err);
2852
+ } catch (err2) {
2853
+ console.error("[host-toolbar] onReady handler threw:", err2);
2827
2854
  }
2828
2855
  }
2829
2856
  function fireReadyHandlers() {
@@ -3228,8 +3255,8 @@ async function handleCustomApiBridgeRequest(apis, req) {
3228
3255
  try {
3229
3256
  const result = req.op === "list" ? apis.list() : await apis.invoke(req.name, req.params);
3230
3257
  return { id: req.id, result };
3231
- } catch (err) {
3232
- return { id: req.id, error: err instanceof Error ? err.message : String(err) };
3258
+ } catch (err2) {
3259
+ return { id: req.id, error: err2 instanceof Error ? err2.message : String(err2) };
3233
3260
  }
3234
3261
  }
3235
3262
  function createSimulatorApiRegistry() {
@@ -3286,8 +3313,8 @@ function registerMiniappSessionConfigurator(fn) {
3286
3313
  for (const partition of configuredPartitions) {
3287
3314
  try {
3288
3315
  fn(electron.session.fromPartition(partition), partition);
3289
- } catch (err) {
3290
- console.warn("[miniapp-partition] configurator failed for", partition, err);
3316
+ } catch (err2) {
3317
+ console.warn("[miniapp-partition] configurator failed for", partition, err2);
3291
3318
  }
3292
3319
  }
3293
3320
  return () => {
@@ -3303,8 +3330,8 @@ function configureMiniappSession(partition) {
3303
3330
  for (const fn of configurators) {
3304
3331
  try {
3305
3332
  fn(sess, partition);
3306
- } catch (err) {
3307
- console.warn("[miniapp-partition] configurator failed for", partition, err);
3333
+ } catch (err2) {
3334
+ console.warn("[miniapp-partition] configurator failed for", partition, err2);
3308
3335
  }
3309
3336
  }
3310
3337
  return sess;
@@ -3457,7 +3484,7 @@ function createNativeSimulatorView(ctx, reconciler, deps) {
3457
3484
  }
3458
3485
  try {
3459
3486
  if (!nativeSimulatorView.webContents.isDestroyed()) {
3460
- ctx.bridge?.disposeSessionsForSimulator?.(nativeSimulatorView.webContents.id)?.catch((err) => console.warn(`[view-manager] dispose sessions (${label}) failed:`, err));
3487
+ ctx.bridge?.disposeSessionsForSimulator?.(nativeSimulatorView.webContents.id)?.catch((err2) => console.warn(`[view-manager] dispose sessions (${label}) failed:`, err2));
3461
3488
  nativeSimulatorView.webContents.close();
3462
3489
  }
3463
3490
  } catch {
@@ -3597,8 +3624,8 @@ function createNativeSimulatorView(ctx, reconciler, deps) {
3597
3624
  e.preventDefault();
3598
3625
  }
3599
3626
  });
3600
- void simWc.loadURL(simulatorUrl).catch((err) => {
3601
- console.error("[workbench] attachNativeSimulator \u2014 loadURL failed", err);
3627
+ void simWc.loadURL(simulatorUrl).catch((err2) => {
3628
+ console.error("[workbench] attachNativeSimulator \u2014 loadURL failed", err2);
3602
3629
  });
3603
3630
  simulatorWebContentsId = simWc.id;
3604
3631
  ctx.networkForward?.attachSimulator(simWc);
@@ -3840,8 +3867,8 @@ function validateProjectDir(dirPath) {
3840
3867
  const resolvedRoot = path10.resolve(dirPath, cfg.miniprogramRoot);
3841
3868
  return `\u8BE5\u76EE\u5F55\u7F3A\u5C11 app.json\uFF0Cproject.config.json \u4E2D\u6307\u5B9A\u4E86 miniprogramRoot: "${cfg.miniprogramRoot}"\uFF0C\u8BF7\u5BFC\u5165 ${resolvedRoot}`;
3842
3869
  }
3843
- } catch (err) {
3844
- log2.warn("Failed to parse project.config.json", err);
3870
+ } catch (err2) {
3871
+ log2.warn("Failed to parse project.config.json", err2);
3845
3872
  }
3846
3873
  }
3847
3874
  return "\u8BE5\u76EE\u5F55\u7F3A\u5C11 app.json\uFF0C\u8BF7\u9009\u62E9\u5305\u542B\u5C0F\u7A0B\u5E8F\u6E90\u7801\u7684\u76EE\u5F55";
@@ -3857,8 +3884,8 @@ function addProject(dirPath) {
3857
3884
  const cfg = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
3858
3885
  if (cfg.projectname) name = cfg.projectname;
3859
3886
  }
3860
- } catch (err) {
3861
- log2.warn("Failed to read project name from config", err);
3887
+ } catch (err2) {
3888
+ log2.warn("Failed to read project name from config", err2);
3862
3889
  }
3863
3890
  const project = { name, path: dirPath, lastOpened: null };
3864
3891
  const idx = projects.findIndex((p) => p.path === dirPath);
@@ -4008,8 +4035,8 @@ function createWorkspaceService(ctx) {
4008
4035
  function bestEffort(label, fn) {
4009
4036
  try {
4010
4037
  fn();
4011
- } catch (err) {
4012
- console.warn(`[workspace] ${label} failed (non-fatal):`, err);
4038
+ } catch (err2) {
4039
+ console.warn(`[workspace] ${label} failed (non-fatal):`, err2);
4013
4040
  }
4014
4041
  }
4015
4042
  async function disposeSession() {
@@ -4023,8 +4050,8 @@ function createWorkspaceService(ctx) {
4023
4050
  (_, reject) => setTimeout(() => reject(new Error("session close timed out")), 5e3)
4024
4051
  )
4025
4052
  ]);
4026
- } catch (err) {
4027
- console.error("[workspace] session close failed:", err);
4053
+ } catch (err2) {
4054
+ console.error("[workspace] session close failed:", err2);
4028
4055
  }
4029
4056
  }
4030
4057
  async function runOpenTeardown(mySeq) {
@@ -4065,16 +4092,16 @@ function createWorkspaceService(ctx) {
4065
4092
  fileTypes: ctx.fileTypes,
4066
4093
  watch: compile.watch,
4067
4094
  onRebuild: () => sendStatus("ready", "\u7F16\u8BD1\u5B8C\u6210\uFF0C\u5DF2\u91CD\u542F", true),
4068
- onBuildError: (err) => sendStatus("error", String(err)),
4095
+ onBuildError: (err2) => sendStatus("error", String(err2)),
4069
4096
  onLog: (entry) => {
4070
4097
  if (sessionGeneration !== logGeneration) return;
4071
4098
  ctx.notify.compileLog({ stream: entry.stream, text: entry.text, at: Date.now() });
4072
4099
  }
4073
4100
  });
4074
4101
  return { session: session5 };
4075
- } catch (err) {
4102
+ } catch (err2) {
4076
4103
  clearSimulatorServicewechatReferer();
4077
- return { error: String(err) };
4104
+ return { error: String(err2) };
4078
4105
  }
4079
4106
  }
4080
4107
  async function rejectInvalidAppId(session5) {
@@ -4112,8 +4139,8 @@ function createWorkspaceService(ctx) {
4112
4139
  if (ctx.onBeforeOpenProject) {
4113
4140
  try {
4114
4141
  await ctx.onBeforeOpenProject(projectPath);
4115
- } catch (err) {
4116
- const error = err instanceof Error ? err.message : String(err);
4142
+ } catch (err2) {
4143
+ const error = err2 instanceof Error ? err2.message : String(err2);
4117
4144
  sendStatus("error", error);
4118
4145
  return { success: false, error };
4119
4146
  }
@@ -4607,51 +4634,60 @@ import path14 from "path";
4607
4634
  import fs7 from "node:fs";
4608
4635
  import path13 from "node:path";
4609
4636
  var DEFAULT_TEMPLATE_ID = "blank";
4610
- async function createProject(input, ctx) {
4611
- const name = (input.name ?? "").trim();
4637
+ function resolveName(rawName) {
4638
+ const name = (rawName ?? "").trim();
4612
4639
  if (name.length === 0) {
4613
4640
  throw new Error("Project name cannot be empty");
4614
4641
  }
4615
- const target = input.path;
4642
+ return name;
4643
+ }
4644
+ function validateTargetPath(target) {
4616
4645
  if (!target || typeof target !== "string") {
4617
4646
  throw new Error("Project path is required");
4618
4647
  }
4619
- if (fs7.existsSync(target)) {
4620
- const stat = fs7.statSync(target);
4621
- if (!stat.isDirectory()) {
4622
- throw new Error(`Project path exists and is not a directory: ${target}`);
4623
- }
4624
- const entries = fs7.readdirSync(target);
4625
- if (entries.length > 0) {
4626
- throw new Error(
4627
- `Project path is not empty (refusing to overwrite): ${target}`
4628
- );
4629
- }
4648
+ if (!fs7.existsSync(target)) return;
4649
+ const stat = fs7.statSync(target);
4650
+ if (!stat.isDirectory()) {
4651
+ throw new Error(`Project path exists and is not a directory: ${target}`);
4630
4652
  }
4631
- const templateId = input.templateId ?? DEFAULT_TEMPLATE_ID;
4632
- const template = ctx.templates.find((t) => t.id === templateId);
4653
+ const entries = fs7.readdirSync(target);
4654
+ if (entries.length > 0) {
4655
+ throw new Error(
4656
+ `Project path is not empty (refusing to overwrite): ${target}`
4657
+ );
4658
+ }
4659
+ }
4660
+ function resolveTemplate(templates, templateId) {
4661
+ const id = templateId ?? DEFAULT_TEMPLATE_ID;
4662
+ const template = templates.find((t) => t.id === id);
4633
4663
  if (!template) {
4634
- throw new Error(`Template not found: ${templateId}`);
4664
+ throw new Error(`Template not found: ${id}`);
4635
4665
  }
4636
4666
  if (!template.source && !template.generate) {
4637
4667
  throw new Error(
4638
- `Template '${templateId}' has neither a source directory nor a generate function`
4668
+ `Template '${id}' has neither a source directory nor a generate function`
4639
4669
  );
4640
4670
  }
4671
+ return template;
4672
+ }
4673
+ async function materializeTemplate(target, template, name) {
4641
4674
  fs7.mkdirSync(target, { recursive: true });
4642
4675
  if (template.generate) {
4643
4676
  await template.generate(target, { name });
4644
- } else if (template.source) {
4645
- if (!fs7.existsSync(template.source.path)) {
4646
- throw new Error(
4647
- `Template source missing on disk: ${template.source.path}`
4648
- );
4649
- }
4650
- fs7.cpSync(template.source.path, target, {
4651
- recursive: true,
4652
- force: true
4653
- });
4677
+ return;
4678
+ }
4679
+ if (!template.source) return;
4680
+ if (!fs7.existsSync(template.source.path)) {
4681
+ throw new Error(
4682
+ `Template source missing on disk: ${template.source.path}`
4683
+ );
4654
4684
  }
4685
+ fs7.cpSync(template.source.path, target, {
4686
+ recursive: true,
4687
+ force: true
4688
+ });
4689
+ }
4690
+ function writeProjectConfig(target, name) {
4655
4691
  const cfgPath = path13.join(target, "project.config.json");
4656
4692
  let cfg = {};
4657
4693
  if (fs7.existsSync(cfgPath)) {
@@ -4663,6 +4699,14 @@ async function createProject(input, ctx) {
4663
4699
  }
4664
4700
  cfg.projectname = name;
4665
4701
  fs7.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
4702
+ }
4703
+ async function createProject(input, ctx) {
4704
+ const name = resolveName(input.name);
4705
+ const target = input.path;
4706
+ validateTargetPath(target);
4707
+ const template = resolveTemplate(ctx.templates, input.templateId);
4708
+ await materializeTemplate(target, template, name);
4709
+ writeProjectConfig(target, name);
4666
4710
  const created = await ctx.projectsProvider.addProject(target);
4667
4711
  return created;
4668
4712
  }
@@ -4726,15 +4770,15 @@ function registerProjectsIpc(ctx) {
4726
4770
  templates: ctx.projectTemplates ?? [],
4727
4771
  projectsProvider: ctx.projectsProvider
4728
4772
  });
4729
- } catch (err) {
4773
+ } catch (err2) {
4730
4774
  await dialog.showMessageBox(ctx.windows.mainWindow, {
4731
4775
  type: "error",
4732
4776
  title: "\u521B\u5EFA\u9879\u76EE\u5931\u8D25",
4733
4777
  message: "\u65E0\u6CD5\u521B\u5EFA\u9879\u76EE",
4734
- detail: err instanceof Error ? err.message : String(err),
4778
+ detail: err2 instanceof Error ? err2.message : String(err2),
4735
4779
  buttons: ["\u786E\u5B9A"]
4736
4780
  });
4737
- throw err;
4781
+ throw err2;
4738
4782
  }
4739
4783
  try {
4740
4784
  const settings = loadWorkbenchSettings();
@@ -4742,8 +4786,8 @@ function registerProjectsIpc(ctx) {
4742
4786
  if (newBase && settings.lastCreateBaseDir !== newBase) {
4743
4787
  saveWorkbenchSettings({ ...settings, lastCreateBaseDir: newBase });
4744
4788
  }
4745
- } catch (err) {
4746
- console.warn("[projects:create] failed to persist lastCreateBaseDir", err);
4789
+ } catch (err2) {
4790
+ console.warn("[projects:create] failed to persist lastCreateBaseDir", err2);
4747
4791
  }
4748
4792
  return project;
4749
4793
  }).handle(ProjectsChannel.GetCreateDefaults, () => {
@@ -4984,7 +5028,7 @@ async function startDiminaResourceServer(rootDir) {
4984
5028
  baseUrl: `http://127.0.0.1:${address.port}/`,
4985
5029
  close() {
4986
5030
  return new Promise((resolve, reject) => {
4987
- server.close((err) => err ? reject(err) : resolve());
5031
+ server.close((err2) => err2 ? reject(err2) : resolve());
4988
5032
  });
4989
5033
  }
4990
5034
  };
@@ -6081,27 +6125,33 @@ function summarizeBridgeMsg(payload) {
6081
6125
  return parts.length > 0 ? parts.join(" ") : void 0;
6082
6126
  }
6083
6127
  var API_CALL_TIMEOUT_MS = 5e3;
6128
+ function findAppSessionByAppId(state, appId) {
6129
+ let match;
6130
+ for (const ap of state.appSessions.values()) if (ap.appId === appId) match = ap;
6131
+ return match;
6132
+ }
6133
+ function resolveFallbackAppSession(state) {
6134
+ let last;
6135
+ const distinctAppIds = /* @__PURE__ */ new Set();
6136
+ for (const ap of state.appSessions.values()) {
6137
+ last = ap;
6138
+ distinctAppIds.add(ap.appId);
6139
+ }
6140
+ return distinctAppIds.size <= 1 ? last : void 0;
6141
+ }
6084
6142
  function resolveCurrentApp(state, ctx, appId) {
6085
6143
  if (appId) {
6086
- let match;
6087
- for (const ap of state.appSessions.values()) if (ap.appId === appId) match = ap;
6144
+ const match = findAppSessionByAppId(state, appId);
6088
6145
  if (match) return match;
6089
6146
  }
6090
6147
  const appInfo = ctx.workspace?.getSession?.()?.appInfo;
6091
6148
  const activeAppId = appInfo?.appId;
6092
6149
  if (activeAppId) {
6093
- let match;
6094
- for (const ap of state.appSessions.values()) if (ap.appId === activeAppId) match = ap;
6150
+ const match = findAppSessionByAppId(state, activeAppId);
6095
6151
  if (match) return match;
6096
6152
  }
6097
6153
  if (ctx.workspace?.isClosing?.()) return void 0;
6098
- let last;
6099
- const distinctAppIds = /* @__PURE__ */ new Set();
6100
- for (const ap of state.appSessions.values()) {
6101
- last = ap;
6102
- distinctAppIds.add(ap.appId);
6103
- }
6104
- return distinctAppIds.size <= 1 ? last : void 0;
6154
+ return resolveFallbackAppSession(state);
6105
6155
  }
6106
6156
  function installBridgeRouter(ctx) {
6107
6157
  const state = {
@@ -6841,99 +6891,108 @@ var APP_LIFECYCLE_UNREGISTER = {
6841
6891
  offAppHide: "onAppHide",
6842
6892
  offError: "onError"
6843
6893
  };
6844
- async function handleSimulatorApi(state, ap, page, body, ctx) {
6845
- const name = String(body.name ?? "");
6846
- const params = normalizeParams(body.params);
6847
- if (NAV_BAR_API_NAMES.has(name)) {
6848
- if (!ap.simulatorWc.isDestroyed()) {
6849
- ap.simulatorWc.send(SIMULATOR_EVENTS.NAV_BAR, {
6850
- bridgeId: page.bridgeId,
6851
- name,
6852
- params
6853
- });
6854
- }
6855
- const successResult = { errMsg: `${name}:ok` };
6856
- sendCallback(ap, params.success, successResult);
6857
- sendCallback(ap, params.complete, successResult);
6858
- return;
6859
- }
6860
- if (NAV_ACTION_NAMES.has(name)) {
6861
- const payload = {
6862
- appSessionId: ap.appSessionId,
6894
+ function handleNavBarApi(ap, page, name, params) {
6895
+ if (!ap.simulatorWc.isDestroyed()) {
6896
+ ap.simulatorWc.send(SIMULATOR_EVENTS.NAV_BAR, {
6863
6897
  bridgeId: page.bridgeId,
6864
6898
  name,
6865
- params,
6866
- callbacks: extractCallbacks(params)
6867
- };
6868
- if (!ap.simulatorWc.isDestroyed()) {
6869
- ap.simulatorWc.send(SIMULATOR_EVENTS.NAV_ACTION, payload);
6870
- } else {
6871
- const fail = { errMsg: `${name}:fail simulator window destroyed` };
6872
- sendCallback(ap, params.fail, fail);
6873
- sendCallback(ap, params.complete, fail);
6874
- }
6875
- return;
6899
+ params
6900
+ });
6876
6901
  }
6877
- if (TAB_ACTION_NAMES.has(name)) {
6878
- const payload = {
6879
- appSessionId: ap.appSessionId,
6880
- bridgeId: page.bridgeId,
6881
- name,
6882
- params,
6883
- callbacks: extractCallbacks(params)
6884
- };
6885
- if (!ap.simulatorWc.isDestroyed()) {
6886
- ap.simulatorWc.send(SIMULATOR_EVENTS.TAB_ACTION, payload);
6887
- } else {
6888
- const fail = { errMsg: `${name}:fail simulator window destroyed` };
6889
- sendCallback(ap, params.fail, fail);
6890
- sendCallback(ap, params.complete, fail);
6891
- }
6902
+ const successResult = { errMsg: `${name}:ok` };
6903
+ sendCallback(ap, params.success, successResult);
6904
+ sendCallback(ap, params.complete, successResult);
6905
+ }
6906
+ function sendActionOrFail(ap, eventName, payload, name, params) {
6907
+ if (!ap.simulatorWc.isDestroyed()) {
6908
+ ap.simulatorWc.send(eventName, payload);
6892
6909
  return;
6893
6910
  }
6911
+ const fail = { errMsg: `${name}:fail simulator window destroyed` };
6912
+ sendCallback(ap, params.fail, fail);
6913
+ sendCallback(ap, params.complete, fail);
6914
+ }
6915
+ function handleNavActionApi(ap, page, name, params) {
6916
+ const payload = {
6917
+ appSessionId: ap.appSessionId,
6918
+ bridgeId: page.bridgeId,
6919
+ name,
6920
+ params,
6921
+ callbacks: extractCallbacks(params)
6922
+ };
6923
+ sendActionOrFail(ap, SIMULATOR_EVENTS.NAV_ACTION, payload, name, params);
6924
+ }
6925
+ function handleTabActionApi(ap, page, name, params) {
6926
+ const payload = {
6927
+ appSessionId: ap.appSessionId,
6928
+ bridgeId: page.bridgeId,
6929
+ name,
6930
+ params,
6931
+ callbacks: extractCallbacks(params)
6932
+ };
6933
+ sendActionOrFail(ap, SIMULATOR_EVENTS.TAB_ACTION, payload, name, params);
6934
+ }
6935
+ function handleAppLifecycleToggle(state, ap, name, params) {
6894
6936
  const lifecycleRegister = APP_LIFECYCLE_REGISTER[name];
6895
6937
  if (lifecycleRegister) {
6896
6938
  state.appLifecycle.register(ap.appSessionId, lifecycleRegister, params.success);
6897
- return;
6939
+ return true;
6898
6940
  }
6899
6941
  const lifecycleUnregister = APP_LIFECYCLE_UNREGISTER[name];
6900
6942
  if (lifecycleUnregister) {
6901
6943
  state.appLifecycle.unregister(ap.appSessionId, lifecycleUnregister, params.success);
6944
+ return true;
6945
+ }
6946
+ return false;
6947
+ }
6948
+ function handlePageScrollApi(ap, page, params) {
6949
+ const renderWc = page.renderWc;
6950
+ if (renderWc && !renderWc.isDestroyed()) {
6951
+ void renderWc.executeJavaScript(buildPageScrollScript(params)).catch(() => {
6952
+ });
6953
+ }
6954
+ const successResult = { errMsg: "pageScrollTo:ok" };
6955
+ sendCallback(ap, params.success, successResult);
6956
+ sendCallback(ap, params.complete, successResult);
6957
+ }
6958
+ async function invokeSimulatorApiAndCallback(ap, name, params, invoke) {
6959
+ try {
6960
+ const result = await invoke();
6961
+ sendCallback(ap, params.success, result);
6962
+ sendCallback(ap, params.complete, result);
6963
+ } catch (error) {
6964
+ const failResult = { errMsg: `${name}:fail ${error instanceof Error ? error.message : String(error)}` };
6965
+ sendCallback(ap, params.fail, failResult);
6966
+ sendCallback(ap, params.complete, failResult);
6967
+ }
6968
+ }
6969
+ async function handleSimulatorApi(state, ap, page, body, ctx) {
6970
+ const name = String(body.name ?? "");
6971
+ const params = normalizeParams(body.params);
6972
+ if (NAV_BAR_API_NAMES.has(name)) {
6973
+ handleNavBarApi(ap, page, name, params);
6974
+ return;
6975
+ }
6976
+ if (NAV_ACTION_NAMES.has(name)) {
6977
+ handleNavActionApi(ap, page, name, params);
6902
6978
  return;
6903
6979
  }
6980
+ if (TAB_ACTION_NAMES.has(name)) {
6981
+ handleTabActionApi(ap, page, name, params);
6982
+ return;
6983
+ }
6984
+ if (handleAppLifecycleToggle(state, ap, name, params)) return;
6904
6985
  if (name === "pageScrollTo") {
6905
- const renderWc = page.renderWc;
6906
- if (renderWc && !renderWc.isDestroyed()) {
6907
- void renderWc.executeJavaScript(buildPageScrollScript(params)).catch(() => {
6908
- });
6909
- }
6910
- const successResult = { errMsg: "pageScrollTo:ok" };
6911
- sendCallback(ap, params.success, successResult);
6912
- sendCallback(ap, params.complete, successResult);
6986
+ handlePageScrollApi(ap, page, params);
6913
6987
  return;
6914
6988
  }
6915
6989
  if (ctx.storageApi && STORAGE_API_NAMES.has(name)) {
6916
- try {
6917
- const result = await ctx.storageApi.invoke(ap.appId, name, params);
6918
- sendCallback(ap, params.success, result);
6919
- sendCallback(ap, params.complete, result);
6920
- } catch (error) {
6921
- const failResult = { errMsg: `${name}:fail ${error instanceof Error ? error.message : String(error)}` };
6922
- sendCallback(ap, params.fail, failResult);
6923
- sendCallback(ap, params.complete, failResult);
6924
- }
6990
+ const storageApi = ctx.storageApi;
6991
+ await invokeSimulatorApiAndCallback(ap, name, params, () => storageApi.invoke(ap.appId, name, params));
6925
6992
  return;
6926
6993
  }
6927
6994
  if (ctx.simulatorApis.has(name)) {
6928
- try {
6929
- const result = await ctx.simulatorApis.invoke(name, params);
6930
- sendCallback(ap, params.success, result);
6931
- sendCallback(ap, params.complete, result);
6932
- } catch (error) {
6933
- const failResult = { errMsg: `${name}:fail ${error instanceof Error ? error.message : String(error)}` };
6934
- sendCallback(ap, params.fail, failResult);
6935
- sendCallback(ap, params.complete, failResult);
6936
- }
6995
+ await invokeSimulatorApiAndCallback(ap, name, params, () => ctx.simulatorApis.invoke(name, params));
6937
6996
  return;
6938
6997
  }
6939
6998
  forwardApiCallToSimulator(state, ap, page, name, params);
@@ -7152,20 +7211,14 @@ function disposePageSession(state, ap, page) {
7152
7211
  ap.pages.delete(page.bridgeId);
7153
7212
  state.pageSessions.delete(page.bridgeId);
7154
7213
  }
7155
- async function disposeAppSession(state, appSessionId, opts = {}) {
7156
- const ap = state.appSessions.get(appSessionId);
7157
- if (!ap) return;
7158
- state.appSessions.delete(appSessionId);
7159
- const registryHandle = ap.registryHandle;
7160
- ap.registryHandle = null;
7161
- void registryHandle?.dispose();
7162
- state.appLifecycle.dispose(appSessionId);
7163
- state.evictAppDataBridges(ap);
7214
+ function drainPendingApiCallsForSession(state, appSessionId) {
7164
7215
  for (const [requestId, pending] of state.pendingApiCalls) {
7165
7216
  if (pending.appSessionId !== appSessionId) continue;
7166
7217
  clearTimeout(pending.timer);
7167
7218
  state.pendingApiCalls.delete(requestId);
7168
7219
  }
7220
+ }
7221
+ function closeSessionPages(state, ap) {
7169
7222
  for (const page of ap.pages.values()) {
7170
7223
  if (page.renderWc && !page.renderWc.isDestroyed()) {
7171
7224
  state.wcIdToBridgeId.delete(page.renderWc.id);
@@ -7176,8 +7229,8 @@ async function disposeAppSession(state, appSessionId, opts = {}) {
7176
7229
  }
7177
7230
  state.pageSessions.delete(page.bridgeId);
7178
7231
  }
7179
- ap.pages.clear();
7180
- ap.listenerBag.dispose();
7232
+ }
7233
+ async function releaseServiceWindow(state, ap, opts) {
7181
7234
  if (ap.poolEntryId !== null && state.pool && !opts.serviceAlreadyClosed) {
7182
7235
  state.connections.reset(ap.serviceWc.id);
7183
7236
  if (ap.onServiceBoot && !ap.serviceWindow.isDestroyed()) {
@@ -7191,6 +7244,8 @@ async function disposeAppSession(state, appSessionId, opts = {}) {
7191
7244
  } else if (!opts.serviceAlreadyClosed && !ap.serviceWindow.isDestroyed()) {
7192
7245
  ap.serviceWindow.close();
7193
7246
  }
7247
+ }
7248
+ function unbindSessionFromSharedMaps(state, ap, appSessionId) {
7194
7249
  if (state.serviceWcIdToAppSessionId.get(ap.serviceWc.id) === appSessionId) {
7195
7250
  state.serviceWcIdToAppSessionId.delete(ap.serviceWc.id);
7196
7251
  }
@@ -7199,6 +7254,22 @@ async function disposeAppSession(state, appSessionId, opts = {}) {
7199
7254
  hosted.delete(appSessionId);
7200
7255
  if (hosted.size === 0) state.simulatorWcIdToAppSessionIds.delete(ap.simulatorWc.id);
7201
7256
  }
7257
+ }
7258
+ async function disposeAppSession(state, appSessionId, opts = {}) {
7259
+ const ap = state.appSessions.get(appSessionId);
7260
+ if (!ap) return;
7261
+ state.appSessions.delete(appSessionId);
7262
+ const registryHandle = ap.registryHandle;
7263
+ ap.registryHandle = null;
7264
+ void registryHandle?.dispose();
7265
+ state.appLifecycle.dispose(appSessionId);
7266
+ state.evictAppDataBridges(ap);
7267
+ drainPendingApiCallsForSession(state, appSessionId);
7268
+ closeSessionPages(state, ap);
7269
+ ap.pages.clear();
7270
+ ap.listenerBag.dispose();
7271
+ await releaseServiceWindow(state, ap, opts);
7272
+ unbindSessionFromSharedMaps(state, ap, appSessionId);
7202
7273
  if (ap.resourceServer) {
7203
7274
  await ap.resourceServer.close().catch((error) => {
7204
7275
  console.warn("[bridge-router] resource server close failed:", error);
@@ -7513,89 +7584,98 @@ appHandlers["App.getPageStack"] = async (ctx) => {
7513
7584
  }
7514
7585
  return { pageStack };
7515
7586
  };
7516
- appHandlers["App.callWxMethod"] = async (ctx, params) => {
7517
- const method = params.method;
7518
- const args = params.args || [];
7519
- if (ctx.bridge?.isNativeHost()) {
7520
- const serviceWc = ctx.bridge.getServiceWc();
7521
- if (!serviceWc) throw new Error("Service host not connected");
7522
- if (NAV_METHODS.has(method) || method === "navigateBack") {
7523
- const arg = method === "navigateBack" ? args[0] ?? { delta: 1 } : args[0] ?? {};
7524
- const since = ctx.bridge.getActiveBridgeId();
7525
- await serviceWc.executeJavaScript(`wx.${method}(${JSON.stringify(arg)})`);
7526
- const timeoutMs = method === "navigateBack" ? 1500 : 2e3;
7527
- await waitForActivePage(ctx.bridge, {
7528
- since,
7529
- timeoutMs,
7530
- onTimeout: () => console.warn(`[automation] ${method}: activePage signal not seen within ${timeoutMs}ms \u2014 proceeding on timeout floor`)
7531
- });
7532
- return { result: void 0 };
7533
- }
7534
- const argsStr2 = args.map((a) => JSON.stringify(a)).join(", ");
7535
- const result2 = await serviceWc.executeJavaScript(`
7536
- new Promise((resolve, reject) => {
7587
+ async function runNativeHostNav(bridge, serviceWc, method, args) {
7588
+ const arg = method === "navigateBack" ? args[0] ?? { delta: 1 } : args[0] ?? {};
7589
+ const since = bridge.getActiveBridgeId();
7590
+ await serviceWc.executeJavaScript(`wx.${method}(${JSON.stringify(arg)})`);
7591
+ const timeoutMs = method === "navigateBack" ? 1500 : 2e3;
7592
+ await waitForActivePage(bridge, {
7593
+ since,
7594
+ timeoutMs,
7595
+ onTimeout: () => console.warn(`[automation] ${method}: activePage signal not seen within ${timeoutMs}ms \u2014 proceeding on timeout floor`)
7596
+ });
7597
+ return { result: void 0 };
7598
+ }
7599
+ async function runNativeHostGeneric(serviceWc, method, args) {
7600
+ const argsStr = args.map((a) => JSON.stringify(a)).join(", ");
7601
+ const result = await serviceWc.executeJavaScript(`
7602
+ new Promise((resolve, reject) => {
7603
+ try {
7604
+ if (typeof wx === 'undefined' || typeof wx[${JSON.stringify(method)}] !== 'function') {
7605
+ reject(new Error('wx.' + ${JSON.stringify(method)} + ' is not a function in service host'))
7606
+ return
7607
+ }
7608
+ resolve(wx.${method}(${argsStr}))
7609
+ } catch (e) { reject(e && e.message ? e.message : String(e)) }
7610
+ })
7611
+ `);
7612
+ return { result };
7613
+ }
7614
+ async function runNativeHostWxMethod(bridge, method, args) {
7615
+ const serviceWc = bridge.getServiceWc();
7616
+ if (!serviceWc) throw new Error("Service host not connected");
7617
+ if (NAV_METHODS.has(method) || method === "navigateBack") {
7618
+ return runNativeHostNav(bridge, serviceWc, method, args);
7619
+ }
7620
+ return runNativeHostGeneric(serviceWc, method, args);
7621
+ }
7622
+ async function clickNavTarget(ctx, cleanUrl) {
7623
+ return evalInSim(ctx, `(() => {
7624
+ const iframes = Array.from(document.querySelectorAll('iframe'))
7625
+ const visible = iframes.filter((f) => {
7626
+ const cs = window.getComputedStyle(f)
7627
+ if (cs.display === 'none' || cs.visibility === 'hidden') return false
7628
+ const rect = f.getBoundingClientRect()
7629
+ return rect.width > 0 && rect.height > 0
7630
+ })
7631
+ const target = visible[visible.length - 1] || iframes[iframes.length - 1]
7632
+ if (!target || !target.contentDocument) return false
7633
+ const path = ${JSON.stringify(cleanUrl)}
7634
+ const el = Array.from(target.contentDocument.querySelectorAll('[data-path]'))
7635
+ .find((e) => e.getAttribute('data-path') === path)
7636
+ if (el) { el.click(); return true }
7637
+ return false
7638
+ })()`).catch(() => false);
7639
+ }
7640
+ async function invokeNavViaWx(ctx, method, cleanUrl) {
7641
+ const apiName = JSON.stringify(method);
7642
+ const urlJson = JSON.stringify(cleanUrl);
7643
+ await evalInSim(
7644
+ ctx,
7645
+ `(() => {
7646
+ const iframes = Array.from(document.querySelectorAll('iframe'))
7647
+ for (const f of iframes) {
7537
7648
  try {
7538
- if (typeof wx === 'undefined' || typeof wx[${JSON.stringify(method)}] !== 'function') {
7539
- reject(new Error('wx.' + ${JSON.stringify(method)} + ' is not a function in service host'))
7649
+ if (f.contentWindow && f.contentWindow.wx && typeof f.contentWindow.wx[${apiName}] === 'function') {
7650
+ f.contentWindow.wx[${apiName}]({ url: ${urlJson} })
7540
7651
  return
7541
7652
  }
7542
- resolve(wx.${method}(${argsStr2}))
7543
- } catch (e) { reject(e && e.message ? e.message : String(e)) }
7544
- })
7545
- `);
7546
- return { result: result2 };
7547
- }
7548
- if (["navigateTo", "redirectTo", "reLaunch", "switchTab"].includes(method)) {
7549
- const opts = args[0];
7550
- const url = opts?.url;
7551
- if (url) {
7552
- const cleanUrl = url.startsWith("/") ? url : `/${url}`;
7553
- const clicked = await evalInSim(ctx, `(() => {
7554
- const iframes = Array.from(document.querySelectorAll('iframe'))
7555
- const visible = iframes.filter((f) => {
7556
- const cs = window.getComputedStyle(f)
7557
- if (cs.display === 'none' || cs.visibility === 'hidden') return false
7558
- const rect = f.getBoundingClientRect()
7559
- return rect.width > 0 && rect.height > 0
7560
- })
7561
- const target = visible[visible.length - 1] || iframes[iframes.length - 1]
7562
- if (!target || !target.contentDocument) return false
7563
- const path = ${JSON.stringify(cleanUrl)}
7564
- const el = Array.from(target.contentDocument.querySelectorAll('[data-path]'))
7565
- .find((e) => e.getAttribute('data-path') === path)
7566
- if (el) { el.click(); return true }
7567
- return false
7568
- })()`).catch(() => false);
7569
- if (!clicked) {
7570
- const apiName = JSON.stringify(method);
7571
- const urlJson = JSON.stringify(cleanUrl);
7572
- await evalInSim(
7573
- ctx,
7574
- `(() => {
7575
- const iframes = Array.from(document.querySelectorAll('iframe'))
7576
- for (const f of iframes) {
7577
- try {
7578
- if (f.contentWindow && f.contentWindow.wx && typeof f.contentWindow.wx[${apiName}] === 'function') {
7579
- f.contentWindow.wx[${apiName}]({ url: ${urlJson} })
7580
- return
7581
- }
7582
- } catch (_) {}
7583
- }
7584
- if (typeof wx !== 'undefined' && wx && typeof wx[${apiName}] === 'function') {
7585
- wx[${apiName}]({ url: ${urlJson} })
7586
- }
7587
- })()`
7588
- );
7653
+ } catch (_) {}
7589
7654
  }
7590
- await new Promise((r) => setTimeout(r, 2e3));
7591
- }
7592
- return { result: void 0 };
7593
- }
7594
- if (method === "navigateBack") {
7595
- await evalInSim(ctx, "history.back()");
7596
- await new Promise((r) => setTimeout(r, 1500));
7597
- return { result: void 0 };
7655
+ if (typeof wx !== 'undefined' && wx && typeof wx[${apiName}] === 'function') {
7656
+ wx[${apiName}]({ url: ${urlJson} })
7657
+ }
7658
+ })()`
7659
+ );
7660
+ }
7661
+ async function runSimulatorNav(ctx, method, args) {
7662
+ const opts = args[0];
7663
+ const url = opts?.url;
7664
+ if (!url) return { result: void 0 };
7665
+ const cleanUrl = url.startsWith("/") ? url : `/${url}`;
7666
+ const clicked = await clickNavTarget(ctx, cleanUrl);
7667
+ if (!clicked) {
7668
+ await invokeNavViaWx(ctx, method, cleanUrl);
7598
7669
  }
7670
+ await new Promise((r) => setTimeout(r, 2e3));
7671
+ return { result: void 0 };
7672
+ }
7673
+ async function runSimulatorNavigateBack(ctx) {
7674
+ await evalInSim(ctx, "history.back()");
7675
+ await new Promise((r) => setTimeout(r, 1500));
7676
+ return { result: void 0 };
7677
+ }
7678
+ async function runSimulatorGeneric(ctx, method, args) {
7599
7679
  const argsStr = args.map((a) => JSON.stringify(a)).join(", ");
7600
7680
  const result = await evalInSim(ctx, `
7601
7681
  new Promise((resolve, reject) => {
@@ -7608,6 +7688,21 @@ appHandlers["App.callWxMethod"] = async (ctx, params) => {
7608
7688
  })
7609
7689
  `);
7610
7690
  return { result };
7691
+ }
7692
+ appHandlers["App.callWxMethod"] = async (ctx, params) => {
7693
+ const method = params.method;
7694
+ const args = params.args || [];
7695
+ const bridge = ctx.bridge;
7696
+ if (bridge?.isNativeHost()) {
7697
+ return runNativeHostWxMethod(bridge, method, args);
7698
+ }
7699
+ if (NAV_METHODS.has(method)) {
7700
+ return runSimulatorNav(ctx, method, args);
7701
+ }
7702
+ if (method === "navigateBack") {
7703
+ return runSimulatorNavigateBack(ctx);
7704
+ }
7705
+ return runSimulatorGeneric(ctx, method, args);
7611
7706
  };
7612
7707
  appHandlers["App.callFunction"] = async (ctx, params) => {
7613
7708
  const fnDecl = params.functionDeclaration;
@@ -7870,7 +7965,7 @@ async function startAutomationServer(ctx, port = 0) {
7870
7965
  const clients = /* @__PURE__ */ new Set();
7871
7966
  await new Promise((resolve, reject) => {
7872
7967
  wss.once("listening", () => resolve());
7873
- wss.once("error", (err) => reject(err));
7968
+ wss.once("error", (err2) => reject(err2));
7874
7969
  });
7875
7970
  const addr = wss.address();
7876
7971
  const resolvedPort = typeof addr === "object" && addr ? addr.port : port;
@@ -7975,8 +8070,8 @@ async function startAutomationServer(ctx, port = 0) {
7975
8070
  try {
7976
8071
  const result = await handler(ctx, params);
7977
8072
  response = { id, result };
7978
- } catch (err) {
7979
- response = { id, error: { message: err.message || String(err) } };
8073
+ } catch (err2) {
8074
+ response = { id, error: { message: err2.message || String(err2) } };
7980
8075
  }
7981
8076
  }
7982
8077
  if (ws.readyState === ws.OPEN) ws.send(JSON.stringify(response));
@@ -8513,6 +8608,96 @@ function registerContextTools(server) {
8513
8608
 
8514
8609
  // src/main/services/mcp/tools/simulator-tools.ts
8515
8610
  import { z as z5 } from "zod";
8611
+ function ok(text) {
8612
+ return { content: [{ type: "text", text }] };
8613
+ }
8614
+ function err(msg) {
8615
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
8616
+ }
8617
+ async function clickAt(c, x, y) {
8618
+ await c.Input.dispatchMouseEvent({ type: "mousePressed", x, y, button: "left", clickCount: 1 });
8619
+ await c.Input.dispatchMouseEvent({ type: "mouseReleased", x, y, button: "left", clickCount: 1 });
8620
+ }
8621
+ async function tapCoord(c, x, y) {
8622
+ if (typeof x !== "number" || typeof y !== "number") return err("tap_coord requires x and y");
8623
+ await clickAt(c, x, y);
8624
+ return ok(`Tapped at (${x}, ${y})`);
8625
+ }
8626
+ function buildTapSelectorExpression(selector, index) {
8627
+ return `(() => {
8628
+ const payload = ${JSON.stringify({ selector, index })}
8629
+ try {
8630
+ const matches = Array.from(document.querySelectorAll(payload.selector))
8631
+ if (matches.length === 0) {
8632
+ return { ok: false, reason: 'no_match', message: \`selector matched no elements: \${payload.selector}\` }
8633
+ }
8634
+ if (payload.index < 0 || payload.index >= matches.length) {
8635
+ return { ok: false, reason: 'out_of_range', message: \`nth \${payload.index} out of range (matches: \${matches.length})\` }
8636
+ }
8637
+ const element = matches[payload.index]
8638
+ element.scrollIntoView({ block: 'center', inline: 'center' })
8639
+ const rect = element.getBoundingClientRect()
8640
+ if (rect.width === 0 || rect.height === 0) {
8641
+ return { ok: false, reason: 'not_visible', message: \`selector matched an element, but it is not visible or rendered (zero rect): \${payload.selector}[\${payload.index}]\` }
8642
+ }
8643
+ return {
8644
+ ok: true,
8645
+ selector: payload.selector,
8646
+ index: payload.index,
8647
+ x: rect.left + rect.width / 2,
8648
+ y: rect.top + rect.height / 2,
8649
+ rect: { left: rect.left, top: rect.top, width: rect.width, height: rect.height },
8650
+ }
8651
+ } catch (error) {
8652
+ const message = error instanceof Error ? error.message : String(error)
8653
+ return { ok: false, reason: 'selector_error', message: \`invalid selector: \${payload.selector} (\${message})\` }
8654
+ }
8655
+ })()`;
8656
+ }
8657
+ async function tapSelector(c, selector, nth) {
8658
+ if (!selector) return err("tap_selector requires selector");
8659
+ const index = nth ?? 0;
8660
+ const expression = buildTapSelectorExpression(selector, index);
8661
+ const result = await c.Runtime.evaluate({ expression, returnByValue: true, awaitPromise: true });
8662
+ const value = result.result?.value;
8663
+ if (!value) return err(`selector evaluation returned no result: ${selector}[${index}]`);
8664
+ if (!value.ok) return err(value.message);
8665
+ await clickAt(c, value.x, value.y);
8666
+ return ok(`Tapped selector ${value.selector}[${value.index}] at (${value.x}, ${value.y}) within rect (${value.rect.left}, ${value.rect.top}, ${value.rect.width} x ${value.rect.height})`);
8667
+ }
8668
+ async function typeText(c, selector, nth, text) {
8669
+ if (!selector || typeof text !== "string") return err("type requires selector and text");
8670
+ const { root } = await c.DOM.getDocument({ depth: 0 });
8671
+ const { nodeIds } = await c.DOM.querySelectorAll({ nodeId: root.nodeId, selector });
8672
+ if (!nodeIds || nodeIds.length === 0) return err(`selector matched no elements: ${selector}`);
8673
+ const index = nth ?? 0;
8674
+ if (index < 0 || index >= nodeIds.length) return err(`nth ${index} out of range (matches: ${nodeIds.length})`);
8675
+ await c.DOM.focus({ nodeId: nodeIds[index] });
8676
+ try {
8677
+ await c.Input.insertText({ text });
8678
+ } catch {
8679
+ for (const ch of text) {
8680
+ await c.Input.dispatchKeyEvent({ type: "char", text: ch });
8681
+ }
8682
+ }
8683
+ return ok(`Typed ${text.length} char(s) into ${selector}[${index}]`);
8684
+ }
8685
+ async function scrollAt(c, x, y, deltaX, deltaY) {
8686
+ if (typeof x !== "number" || typeof y !== "number" || typeof deltaX !== "number" || typeof deltaY !== "number") {
8687
+ return err("scroll requires x, y, deltaX, deltaY");
8688
+ }
8689
+ await c.Input.dispatchMouseEvent({ type: "mouseWheel", x, y, deltaX, deltaY });
8690
+ return ok(`Scrolled at (${x}, ${y}) by (${deltaX}, ${deltaY})`);
8691
+ }
8692
+ async function dispatchKey(c, key) {
8693
+ if (!key) return err("key requires key");
8694
+ const isSingleChar = key.length === 1;
8695
+ const down = { type: "keyDown", key, code: key };
8696
+ if (isSingleChar) down.text = key;
8697
+ await c.Input.dispatchKeyEvent(down);
8698
+ await c.Input.dispatchKeyEvent({ type: "keyUp", key, code: key });
8699
+ return ok(`Dispatched key ${key}`);
8700
+ }
8516
8701
  function registerSimulatorTools(server) {
8517
8702
  server.tool("simulator_navigate", "Navigate the simulator to a URL, or reload the current page", {
8518
8703
  url: z5.string().optional().describe("URL to navigate to"),
@@ -8542,94 +8727,20 @@ function registerSimulatorTools(server) {
8542
8727
  deltaY: z5.number().optional().describe("Vertical wheel delta for `scroll`")
8543
8728
  }, async ({ action, x, y, selector, nth, text, key, deltaX, deltaY }) => {
8544
8729
  const c = getClient("simulator");
8545
- const err = (msg) => ({ content: [{ type: "text", text: `Error: ${msg}` }], isError: true });
8546
- const clickAt = async (cx, cy) => {
8547
- await c.Input.dispatchMouseEvent({ type: "mousePressed", x: cx, y: cy, button: "left", clickCount: 1 });
8548
- await c.Input.dispatchMouseEvent({ type: "mouseReleased", x: cx, y: cy, button: "left", clickCount: 1 });
8549
- };
8550
- if (action === "tap_coord") {
8551
- if (typeof x !== "number" || typeof y !== "number") return err("tap_coord requires x and y");
8552
- await clickAt(x, y);
8553
- return { content: [{ type: "text", text: `Tapped at (${x}, ${y})` }] };
8554
- }
8555
- if (action === "tap_selector") {
8556
- if (!selector) return err("tap_selector requires selector");
8557
- const index = nth ?? 0;
8558
- const expression = `(() => {
8559
- const payload = ${JSON.stringify({ selector, index })}
8560
- try {
8561
- const matches = Array.from(document.querySelectorAll(payload.selector))
8562
- if (matches.length === 0) {
8563
- return { ok: false, reason: 'no_match', message: \`selector matched no elements: \${payload.selector}\` }
8564
- }
8565
- if (payload.index < 0 || payload.index >= matches.length) {
8566
- return { ok: false, reason: 'out_of_range', message: \`nth \${payload.index} out of range (matches: \${matches.length})\` }
8567
- }
8568
- const element = matches[payload.index]
8569
- element.scrollIntoView({ block: 'center', inline: 'center' })
8570
- const rect = element.getBoundingClientRect()
8571
- if (rect.width === 0 || rect.height === 0) {
8572
- return { ok: false, reason: 'not_visible', message: \`selector matched an element, but it is not visible or rendered (zero rect): \${payload.selector}[\${payload.index}]\` }
8573
- }
8574
- return {
8575
- ok: true,
8576
- selector: payload.selector,
8577
- index: payload.index,
8578
- x: rect.left + rect.width / 2,
8579
- y: rect.top + rect.height / 2,
8580
- rect: { left: rect.left, top: rect.top, width: rect.width, height: rect.height },
8581
- }
8582
- } catch (error) {
8583
- const message = error instanceof Error ? error.message : String(error)
8584
- return { ok: false, reason: 'selector_error', message: \`invalid selector: \${payload.selector} (\${message})\` }
8585
- }
8586
- })()`;
8587
- const result = await c.Runtime.evaluate({ expression, returnByValue: true, awaitPromise: true });
8588
- const value = result.result?.value;
8589
- if (!value) return err(`selector evaluation returned no result: ${selector}[${index}]`);
8590
- if (!value.ok) return err(value.message);
8591
- await clickAt(value.x, value.y);
8592
- return {
8593
- content: [{
8594
- type: "text",
8595
- text: `Tapped selector ${value.selector}[${value.index}] at (${value.x}, ${value.y}) within rect (${value.rect.left}, ${value.rect.top}, ${value.rect.width} x ${value.rect.height})`
8596
- }]
8597
- };
8598
- }
8599
- if (action === "type") {
8600
- if (!selector || typeof text !== "string") return err("type requires selector and text");
8601
- const { root } = await c.DOM.getDocument({ depth: 0 });
8602
- const { nodeIds } = await c.DOM.querySelectorAll({ nodeId: root.nodeId, selector });
8603
- if (!nodeIds || nodeIds.length === 0) return err(`selector matched no elements: ${selector}`);
8604
- const index = nth ?? 0;
8605
- if (index < 0 || index >= nodeIds.length) return err(`nth ${index} out of range (matches: ${nodeIds.length})`);
8606
- await c.DOM.focus({ nodeId: nodeIds[index] });
8607
- try {
8608
- await c.Input.insertText({ text });
8609
- } catch {
8610
- for (const ch of text) {
8611
- await c.Input.dispatchKeyEvent({ type: "char", text: ch });
8612
- }
8613
- }
8614
- return { content: [{ type: "text", text: `Typed ${text.length} char(s) into ${selector}[${index}]` }] };
8615
- }
8616
- if (action === "scroll") {
8617
- if (typeof x !== "number" || typeof y !== "number" || typeof deltaX !== "number" || typeof deltaY !== "number") {
8618
- return err("scroll requires x, y, deltaX, deltaY");
8619
- }
8620
- await c.Input.dispatchMouseEvent({ type: "mouseWheel", x, y, deltaX, deltaY });
8621
- return { content: [{ type: "text", text: `Scrolled at (${x}, ${y}) by (${deltaX}, ${deltaY})` }] };
8622
- }
8623
- if (action === "key") {
8624
- if (!key) return err("key requires key");
8625
- const isSingleChar = key.length === 1;
8626
- const down = { type: "keyDown", key, code: key };
8627
- if (isSingleChar) down.text = key;
8628
- await c.Input.dispatchKeyEvent(down);
8629
- await c.Input.dispatchKeyEvent({ type: "keyUp", key, code: key });
8630
- return { content: [{ type: "text", text: `Dispatched key ${key}` }] };
8730
+ switch (action) {
8731
+ case "tap_coord":
8732
+ return tapCoord(c, x, y);
8733
+ case "tap_selector":
8734
+ return tapSelector(c, selector, nth);
8735
+ case "type":
8736
+ return typeText(c, selector, nth, text);
8737
+ case "scroll":
8738
+ return scrollAt(c, x, y, deltaX, deltaY);
8739
+ case "key":
8740
+ return dispatchKey(c, key);
8741
+ default:
8742
+ return err(`unknown action: ${String(action)}`);
8631
8743
  }
8632
- return err(`unknown action: ${action}`);
8633
8744
  });
8634
8745
  }
8635
8746
 
@@ -8684,13 +8795,13 @@ function startMcpServer(resolvedCdpPort, mcpPort) {
8684
8795
  res.writeHead(404).end();
8685
8796
  }
8686
8797
  });
8687
- httpServer.on("error", (err) => {
8688
- if (err.code === "EADDRINUSE") {
8798
+ httpServer.on("error", (err2) => {
8799
+ if (err2.code === "EADDRINUSE") {
8689
8800
  console.warn(`[MCP] Port ${mcpPort} already in use \u2014 MCP server not started`);
8690
8801
  recordMcpFailed("port-in-use");
8691
8802
  } else {
8692
- console.error("[MCP] Server error:", err);
8693
- recordMcpFailed(err.message);
8803
+ console.error("[MCP] Server error:", err2);
8804
+ recordMcpFailed(err2.message);
8694
8805
  }
8695
8806
  });
8696
8807
  httpServer.listen(mcpPort, "127.0.0.1", () => {
@@ -8706,6 +8817,28 @@ function startMcpServer(resolvedCdpPort, mcpPort) {
8706
8817
 
8707
8818
  // src/main/services/network-forward/index.ts
8708
8819
  import { DisposableRegistry as DisposableRegistry7, toDisposable as toDisposable5 } from "@dimina-kit/electron-deck/main";
8820
+
8821
+ // src/main/services/network-forward/dispatch-batch.ts
8822
+ function packDispatchBatch(queue, maxSingleChars, maxBatchChars) {
8823
+ const batch = [];
8824
+ const chunked = [];
8825
+ let batchChars = 0;
8826
+ let i = 0;
8827
+ for (; i < queue.length; i++) {
8828
+ const msg = queue[i];
8829
+ if (msg.length > maxSingleChars) {
8830
+ if (batch.length > 0) break;
8831
+ chunked.push(msg);
8832
+ continue;
8833
+ }
8834
+ if (batch.length > 0 && batchChars + msg.length > maxBatchChars) break;
8835
+ batch.push(msg);
8836
+ batchChars += msg.length;
8837
+ }
8838
+ return { batch, chunked, remaining: queue.slice(i) };
8839
+ }
8840
+
8841
+ // src/main/services/network-forward/index.ts
8709
8842
  var REWRITE_REQUEST_ID_METHODS = /* @__PURE__ */ new Set([
8710
8843
  "Network.requestWillBeSent",
8711
8844
  "Network.requestWillBeSentExtraInfo",
@@ -8922,21 +9055,8 @@ function createNetworkForwarder(bridge) {
8922
9055
  return;
8923
9056
  }
8924
9057
  if (sink === "idle") beginProbing();
8925
- const batch = [];
8926
- let batchChars = 0;
8927
- let i = 0;
8928
- for (; i < dispatchQueue.length; i++) {
8929
- const msg = dispatchQueue[i];
8930
- if (msg.length > MAX_SINGLE_DISPATCH_CHARS2) {
8931
- if (batch.length > 0) break;
8932
- dispatchChunked(wc, msg);
8933
- continue;
8934
- }
8935
- if (batch.length > 0 && batchChars + msg.length > MAX_BATCH_CHARS) break;
8936
- batch.push(msg);
8937
- batchChars += msg.length;
8938
- }
8939
- const remaining = dispatchQueue.slice(i);
9058
+ const { batch, chunked, remaining } = packDispatchBatch(dispatchQueue, MAX_SINGLE_DISPATCH_CHARS2, MAX_BATCH_CHARS);
9059
+ for (const msg of chunked) dispatchChunked(wc, msg);
8940
9060
  if (batch.length === 0) {
8941
9061
  dispatchQueue = remaining;
8942
9062
  if (dispatchQueue.length > 0) scheduleFlush();
@@ -8952,8 +9072,8 @@ function createNetworkForwarder(bridge) {
8952
9072
  }
8953
9073
  dispatchQueue = remaining;
8954
9074
  if (remaining.length > 0) scheduleFlush();
8955
- wc.executeJavaScript(script, true).then((ok) => {
8956
- if (ok === true) {
9075
+ wc.executeJavaScript(script, true).then((ok2) => {
9076
+ if (ok2 === true) {
8957
9077
  if (sink !== "ready") markReady();
8958
9078
  return;
8959
9079
  }
@@ -9048,8 +9168,8 @@ function createNetworkForwarder(bridge) {
9048
9168
  if (!wc.debugger.isAttached()) {
9049
9169
  wc.debugger.attach("1.3");
9050
9170
  }
9051
- } catch (err) {
9052
- console.warn("[network-forward] debugger.attach failed; simulator network not captured:", err instanceof Error ? err.message : err);
9171
+ } catch (err2) {
9172
+ console.warn("[network-forward] debugger.attach failed; simulator network not captured:", err2 instanceof Error ? err2.message : err2);
9053
9173
  return;
9054
9174
  }
9055
9175
  simWc = wc;
@@ -9145,8 +9265,8 @@ function createNetworkForwarder(bridge) {
9145
9265
  }
9146
9266
  });
9147
9267
  }
9148
- void wc.debugger.sendCommand("Network.enable").catch((err) => {
9149
- console.warn("[network-forward] Network.enable failed:", err instanceof Error ? err.message : err);
9268
+ void wc.debugger.sendCommand("Network.enable").catch((err2) => {
9269
+ console.warn("[network-forward] Network.enable failed:", err2 instanceof Error ? err2.message : err2);
9150
9270
  });
9151
9271
  }
9152
9272
  function maybeFallback(record) {
@@ -9316,41 +9436,46 @@ function setupSimulatorWxml(host, options) {
9316
9436
  }
9317
9437
 
9318
9438
  // src/shared/appdata-accumulator.ts
9319
- function decodeWorkerMessage(message) {
9320
- let payload = message;
9321
- if (typeof payload === "string") {
9322
- try {
9323
- payload = JSON.parse(payload);
9324
- } catch {
9325
- return null;
9326
- }
9439
+ function parseMessagePayload(message) {
9440
+ if (typeof message !== "string") return message;
9441
+ try {
9442
+ return JSON.parse(message);
9443
+ } catch {
9444
+ return null;
9327
9445
  }
9446
+ }
9447
+ function decodeUpdateBatchBody(rawBody) {
9448
+ const body = rawBody;
9449
+ if (!body || typeof body !== "object") return null;
9450
+ if (typeof body.bridgeId !== "string" || !Array.isArray(body.updates)) return null;
9451
+ const out = [];
9452
+ for (const u of body.updates) {
9453
+ if (!u || typeof u.moduleId !== "string") continue;
9454
+ if (!u.moduleId.startsWith("page_")) continue;
9455
+ out.push({ mode: "patch", bridgeId: body.bridgeId, moduleId: u.moduleId, data: u.data });
9456
+ }
9457
+ return out.length > 0 ? out : null;
9458
+ }
9459
+ function decodePageInitBody(moduleId, rawBody) {
9460
+ const body = rawBody;
9461
+ if (!body || typeof body !== "object") return null;
9462
+ if (typeof body.bridgeId !== "string" || typeof body.path !== "string") return null;
9463
+ if (!body.data || typeof body.data !== "object") return null;
9464
+ return [{
9465
+ mode: "init",
9466
+ bridgeId: body.bridgeId,
9467
+ moduleId,
9468
+ componentPath: body.path,
9469
+ data: body.data
9470
+ }];
9471
+ }
9472
+ function decodeWorkerMessage(message) {
9473
+ const payload = parseMessagePayload(message);
9328
9474
  if (!payload || typeof payload !== "object") return null;
9329
9475
  const record = payload;
9330
- if (record.type === "ub") {
9331
- const body = record.body;
9332
- if (!body || typeof body !== "object") return null;
9333
- if (typeof body.bridgeId !== "string" || !Array.isArray(body.updates)) return null;
9334
- const out = [];
9335
- for (const u of body.updates) {
9336
- if (!u || typeof u.moduleId !== "string") continue;
9337
- if (!u.moduleId.startsWith("page_")) continue;
9338
- out.push({ mode: "patch", bridgeId: body.bridgeId, moduleId: u.moduleId, data: u.data });
9339
- }
9340
- return out.length > 0 ? out : null;
9341
- }
9476
+ if (record.type === "ub") return decodeUpdateBatchBody(record.body);
9342
9477
  if (typeof record.type === "string" && record.type.startsWith("page_")) {
9343
- const body = record.body;
9344
- if (!body || typeof body !== "object") return null;
9345
- if (typeof body.bridgeId !== "string" || typeof body.path !== "string") return null;
9346
- if (!body.data || typeof body.data !== "object") return null;
9347
- return [{
9348
- mode: "init",
9349
- bridgeId: body.bridgeId,
9350
- moduleId: record.type,
9351
- componentPath: body.path,
9352
- data: body.data
9353
- }];
9478
+ return decodePageInitBody(record.type, record.body);
9354
9479
  }
9355
9480
  return null;
9356
9481
  }
@@ -9804,38 +9929,51 @@ var EXT_MIME = {
9804
9929
  ".pdf": "application/pdf",
9805
9930
  ".zip": "application/zip"
9806
9931
  };
9932
+ function isPng(head) {
9933
+ return head.length >= 8 && head[0] === 137 && head[1] === 80 && head[2] === 78 && head[3] === 71 && head[4] === 13 && head[5] === 10 && head[6] === 26 && head[7] === 10;
9934
+ }
9935
+ function isJpeg(head) {
9936
+ return head.length >= 3 && head[0] === 255 && head[1] === 216 && head[2] === 255;
9937
+ }
9938
+ function isGif(head) {
9939
+ return head.length >= 6 && head[0] === 71 && head[1] === 73 && head[2] === 70 && head[3] === 56 && (head[4] === 55 || head[4] === 57) && head[5] === 97;
9940
+ }
9941
+ function isWebp(head) {
9942
+ return head.length >= 12 && head[0] === 82 && head[1] === 73 && head[2] === 70 && head[3] === 70 && head[8] === 87 && head[9] === 69 && head[10] === 66 && head[11] === 80;
9943
+ }
9944
+ function isMp4(head) {
9945
+ return head.length >= 12 && head[4] === 102 && head[5] === 116 && head[6] === 121 && head[7] === 112;
9946
+ }
9947
+ function isPdf(head) {
9948
+ return head.length >= 4 && head[0] === 37 && head[1] === 80 && head[2] === 68 && head[3] === 70;
9949
+ }
9950
+ function isBmp(head) {
9951
+ return head.length >= 2 && head[0] === 66 && head[1] === 77;
9952
+ }
9953
+ function isZip(head) {
9954
+ return head.length >= 4 && head[0] === 80 && head[1] === 75 && head[2] === 3 && head[3] === 4;
9955
+ }
9956
+ function isId3Mp3(head) {
9957
+ return head.length >= 3 && head[0] === 73 && head[1] === 68 && head[2] === 51;
9958
+ }
9959
+ function isOgg(head) {
9960
+ return head.length >= 4 && head[0] === 79 && head[1] === 103 && head[2] === 103 && head[3] === 83;
9961
+ }
9962
+ var MAGIC_SNIFFERS = [
9963
+ { mime: "image/png", match: isPng },
9964
+ { mime: "image/jpeg", match: isJpeg },
9965
+ { mime: "image/gif", match: isGif },
9966
+ { mime: "image/webp", match: isWebp },
9967
+ { mime: "video/mp4", match: isMp4 },
9968
+ { mime: "application/pdf", match: isPdf },
9969
+ { mime: "image/bmp", match: isBmp },
9970
+ { mime: "application/zip", match: isZip },
9971
+ { mime: "audio/mpeg", match: isId3Mp3 },
9972
+ { mime: "audio/ogg", match: isOgg }
9973
+ ];
9807
9974
  function sniffMime(head) {
9808
- if (head.length >= 8 && head[0] === 137 && head[1] === 80 && head[2] === 78 && head[3] === 71 && head[4] === 13 && head[5] === 10 && head[6] === 26 && head[7] === 10) {
9809
- return "image/png";
9810
- }
9811
- if (head.length >= 3 && head[0] === 255 && head[1] === 216 && head[2] === 255) {
9812
- return "image/jpeg";
9813
- }
9814
- if (head.length >= 6 && head[0] === 71 && head[1] === 73 && head[2] === 70 && head[3] === 56 && (head[4] === 55 || head[4] === 57) && head[5] === 97) {
9815
- return "image/gif";
9816
- }
9817
- if (head.length >= 12 && head[0] === 82 && head[1] === 73 && head[2] === 70 && head[3] === 70 && head[8] === 87 && head[9] === 69 && head[10] === 66 && head[11] === 80) {
9818
- return "image/webp";
9819
- }
9820
- if (head.length >= 12 && head[4] === 102 && head[5] === 116 && head[6] === 121 && head[7] === 112) {
9821
- return "video/mp4";
9822
- }
9823
- if (head.length >= 4 && head[0] === 37 && head[1] === 80 && head[2] === 68 && head[3] === 70) {
9824
- return "application/pdf";
9825
- }
9826
- if (head.length >= 2 && head[0] === 66 && head[1] === 77) {
9827
- return "image/bmp";
9828
- }
9829
- if (head.length >= 4 && head[0] === 80 && head[1] === 75 && head[2] === 3 && head[3] === 4) {
9830
- return "application/zip";
9831
- }
9832
- if (head.length >= 3 && head[0] === 73 && head[1] === 68 && head[2] === 51) {
9833
- return "audio/mpeg";
9834
- }
9835
- if (head.length >= 4 && head[0] === 79 && head[1] === 103 && head[2] === 103 && head[3] === 83) {
9836
- return "audio/ogg";
9837
- }
9838
- return null;
9975
+ const hit = MAGIC_SNIFFERS.find((sniffer) => sniffer.match(head));
9976
+ return hit ? hit.mime : null;
9839
9977
  }
9840
9978
  function extMime(realPath) {
9841
9979
  const ext = path20.extname(realPath).toLowerCase();
@@ -9852,6 +9990,44 @@ function detectMime(realPath, head) {
9852
9990
  function etagOf(mtimeMs, size) {
9853
9991
  return `W/"${Math.floor(mtimeMs)}-${size}"`;
9854
9992
  }
9993
+ function resolveRange(range, totalSize) {
9994
+ const { start, end } = range;
9995
+ if (!Number.isFinite(start) || !Number.isFinite(end)) {
9996
+ throw new RangeError(`invalid range: ${start}-${end}`);
9997
+ }
9998
+ if (start < 0) throw new RangeError(`range start out of bounds: ${start}`);
9999
+ if (start > end) throw new RangeError(`range start > end: ${start} > ${end}`);
10000
+ if (start >= totalSize) {
10001
+ throw new RangeError(`range start beyond file size: ${start} >= ${totalSize}`);
10002
+ }
10003
+ const clampedEnd = Math.min(end, totalSize - 1);
10004
+ return { sliceStart: start, sliceLen: clampedEnd - start + 1 };
10005
+ }
10006
+ async function readHeadBytes(handle, totalSize) {
10007
+ const head = Buffer.alloc(Math.min(12, totalSize));
10008
+ if (head.length > 0) {
10009
+ await handle.read(head, 0, head.length, 0);
10010
+ }
10011
+ return head;
10012
+ }
10013
+ async function readFullBody(handle, head, totalSize) {
10014
+ const bytes = Buffer.alloc(totalSize);
10015
+ if (totalSize === 0) return bytes;
10016
+ if (totalSize <= head.length) {
10017
+ head.copy(bytes, 0, 0, totalSize);
10018
+ return bytes;
10019
+ }
10020
+ head.copy(bytes, 0, 0, head.length);
10021
+ await handle.read(bytes, head.length, totalSize - head.length, head.length);
10022
+ return bytes;
10023
+ }
10024
+ async function readRangeBody(handle, sliceStart, sliceLen) {
10025
+ const bytes = Buffer.alloc(sliceLen);
10026
+ if (sliceLen > 0) {
10027
+ await handle.read(bytes, 0, sliceLen, sliceStart);
10028
+ }
10029
+ return bytes;
10030
+ }
9855
10031
  async function readDiskFile(realPath, opts) {
9856
10032
  const handle = await fs9.open(realPath, "r");
9857
10033
  try {
@@ -9859,43 +10035,9 @@ async function readDiskFile(realPath, opts) {
9859
10035
  const totalSize = st.size;
9860
10036
  const mtimeMs = st.mtimeMs;
9861
10037
  const range = opts?.range;
9862
- let sliceStart = 0;
9863
- let sliceLen = totalSize;
9864
- if (range) {
9865
- const { start, end } = range;
9866
- if (!Number.isFinite(start) || !Number.isFinite(end)) {
9867
- throw new RangeError(`invalid range: ${start}-${end}`);
9868
- }
9869
- if (start < 0) throw new RangeError(`range start out of bounds: ${start}`);
9870
- if (start > end) throw new RangeError(`range start > end: ${start} > ${end}`);
9871
- if (start >= totalSize) {
9872
- throw new RangeError(`range start beyond file size: ${start} >= ${totalSize}`);
9873
- }
9874
- const clampedEnd = Math.min(end, totalSize - 1);
9875
- sliceStart = start;
9876
- sliceLen = clampedEnd - start + 1;
9877
- }
9878
- const head = Buffer.alloc(Math.min(12, totalSize));
9879
- if (head.length > 0) {
9880
- await handle.read(head, 0, head.length, 0);
9881
- }
9882
- let bytes;
9883
- if (!range) {
9884
- bytes = Buffer.alloc(totalSize);
9885
- if (totalSize > 0) {
9886
- if (totalSize <= head.length) {
9887
- head.copy(bytes, 0, 0, totalSize);
9888
- } else {
9889
- head.copy(bytes, 0, 0, head.length);
9890
- await handle.read(bytes, head.length, totalSize - head.length, head.length);
9891
- }
9892
- }
9893
- } else {
9894
- bytes = Buffer.alloc(sliceLen);
9895
- if (sliceLen > 0) {
9896
- await handle.read(bytes, 0, sliceLen, sliceStart);
9897
- }
9898
- }
10038
+ const { sliceStart, sliceLen } = range ? resolveRange(range, totalSize) : { sliceStart: 0, sliceLen: totalSize };
10039
+ const head = await readHeadBytes(handle, totalSize);
10040
+ const bytes = range ? await readRangeBody(handle, sliceStart, sliceLen) : await readFullBody(handle, head, totalSize);
9899
10041
  return {
9900
10042
  bytes,
9901
10043
  mime: detectMime(realPath, head),
@@ -10069,12 +10211,12 @@ async function enforceSandbox(realPath) {
10069
10211
  throw new Error("sandbox: realPath escapes the user-data base via symlink");
10070
10212
  }
10071
10213
  break;
10072
- } catch (err) {
10073
- if (err.code === "ENOENT") {
10214
+ } catch (err2) {
10215
+ if (err2.code === "ENOENT") {
10074
10216
  probe = path21.dirname(probe);
10075
10217
  continue;
10076
10218
  }
10077
- throw err;
10219
+ throw err2;
10078
10220
  }
10079
10221
  }
10080
10222
  return normalized;
@@ -10330,8 +10472,8 @@ function serveStaticFile(res, root, pathname) {
10330
10472
  res.end("Forbidden");
10331
10473
  return;
10332
10474
  }
10333
- nodeFs2.stat(realFile, (err, stat) => {
10334
- if (err || !stat.isFile()) {
10475
+ nodeFs2.stat(realFile, (err2, stat) => {
10476
+ if (err2 || !stat.isFile()) {
10335
10477
  res.writeHead(404);
10336
10478
  res.end("Not Found");
10337
10479
  return;
@@ -10412,6 +10554,56 @@ function fsErrorStatus(e) {
10412
10554
  if (code === "ENOENT") return 404;
10413
10555
  return 500;
10414
10556
  }
10557
+ async function fsStat(ctx) {
10558
+ const st = await statWithin(ctx.projectRoot, ctx.rel);
10559
+ jsonRes(ctx.res, 200, { type: st.isDirectory() ? 2 : 1, size: st.size, ctime: st.ctimeMs, mtime: st.mtimeMs });
10560
+ }
10561
+ async function fsReaddir(ctx) {
10562
+ const entries = await readdirWithin(ctx.projectRoot, ctx.rel);
10563
+ jsonRes(ctx.res, 200, entries.map((e) => [e.name, e.isDirectory() ? 2 : 1]));
10564
+ }
10565
+ async function fsRead(ctx) {
10566
+ const buf = await readFileBufferWithin(ctx.projectRoot, ctx.rel);
10567
+ ctx.res.writeHead(200, { "Content-Type": "application/octet-stream", "Content-Length": buf.length });
10568
+ ctx.res.end(buf);
10569
+ }
10570
+ async function fsWrite(ctx) {
10571
+ let buf;
10572
+ try {
10573
+ buf = await readBody(ctx.req);
10574
+ } catch (e) {
10575
+ if (e instanceof BodyTooLargeError) return jsonRes(ctx.res, 413, { error: e.message });
10576
+ throw e;
10577
+ }
10578
+ await writeFileWithin(ctx.projectRoot, ctx.rel, buf);
10579
+ ctx.res.writeHead(204);
10580
+ ctx.res.end();
10581
+ }
10582
+ async function fsMkdir(ctx) {
10583
+ await mkdirWithin(ctx.projectRoot, ctx.rel);
10584
+ ctx.res.writeHead(204);
10585
+ ctx.res.end();
10586
+ }
10587
+ async function fsDelete(ctx) {
10588
+ await deleteWithin(ctx.projectRoot, ctx.rel);
10589
+ ctx.res.writeHead(204);
10590
+ ctx.res.end();
10591
+ }
10592
+ async function fsRename(ctx) {
10593
+ const toRel = (ctx.url.searchParams.get("to") ?? "").replace(/^\/+/, "");
10594
+ await renameWithin(ctx.projectRoot, ctx.rel, toRel);
10595
+ ctx.res.writeHead(204);
10596
+ ctx.res.end();
10597
+ }
10598
+ var FS_ACTIONS = {
10599
+ stat: fsStat,
10600
+ readdir: fsReaddir,
10601
+ read: fsRead,
10602
+ write: fsWrite,
10603
+ mkdir: fsMkdir,
10604
+ delete: fsDelete,
10605
+ rename: fsRename
10606
+ };
10415
10607
  async function handleFsRequest(req, res, projectRoot, url) {
10416
10608
  const action = url.pathname.slice("/__fs/".length);
10417
10609
  const rel = (url.searchParams.get("p") ?? "").replace(/^\/+/, "") || ".";
@@ -10421,51 +10613,12 @@ async function handleFsRequest(req, res, projectRoot, url) {
10421
10613
  return jsonRes(res, rejectStatus, { error: "mutating fs action requires a non-GET same-origin request" });
10422
10614
  }
10423
10615
  }
10616
+ const handler = FS_ACTIONS[action];
10617
+ if (!handler) return jsonRes(res, 404, { error: "unknown fs action: " + action });
10424
10618
  try {
10425
- if (action === "stat") {
10426
- const st = await statWithin(projectRoot, rel);
10427
- return jsonRes(res, 200, { type: st.isDirectory() ? 2 : 1, size: st.size, ctime: st.ctimeMs, mtime: st.mtimeMs });
10428
- }
10429
- if (action === "readdir") {
10430
- const entries = await readdirWithin(projectRoot, rel);
10431
- return jsonRes(res, 200, entries.map((e) => [e.name, e.isDirectory() ? 2 : 1]));
10432
- }
10433
- if (action === "read") {
10434
- const buf = await readFileBufferWithin(projectRoot, rel);
10435
- res.writeHead(200, { "Content-Type": "application/octet-stream", "Content-Length": buf.length });
10436
- return void res.end(buf);
10437
- }
10438
- if (action === "write") {
10439
- let buf;
10440
- try {
10441
- buf = await readBody(req);
10442
- } catch (e) {
10443
- if (e instanceof BodyTooLargeError) return jsonRes(res, 413, { error: e.message });
10444
- throw e;
10445
- }
10446
- await writeFileWithin(projectRoot, rel, buf);
10447
- res.writeHead(204);
10448
- return void res.end();
10449
- }
10450
- if (action === "mkdir") {
10451
- await mkdirWithin(projectRoot, rel);
10452
- res.writeHead(204);
10453
- return void res.end();
10454
- }
10455
- if (action === "delete") {
10456
- await deleteWithin(projectRoot, rel);
10457
- res.writeHead(204);
10458
- return void res.end();
10459
- }
10460
- if (action === "rename") {
10461
- const toRel = (url.searchParams.get("to") ?? "").replace(/^\/+/, "");
10462
- await renameWithin(projectRoot, rel, toRel);
10463
- res.writeHead(204);
10464
- return void res.end();
10465
- }
10466
- return jsonRes(res, 404, { error: "unknown fs action: " + action });
10619
+ await handler({ req, res, projectRoot, rel, url });
10467
10620
  } catch (e) {
10468
- return jsonRes(res, fsErrorStatus(e), { error: String(e.message), code: e.code });
10621
+ jsonRes(res, fsErrorStatus(e), { error: String(e.message), code: e.code });
10469
10622
  }
10470
10623
  }
10471
10624
  async function listFilesRecursive(dir, base = "") {
@@ -10598,8 +10751,8 @@ var UpdateManager = class {
10598
10751
  return { hasUpdate: true, info };
10599
10752
  }
10600
10753
  return { hasUpdate: false };
10601
- } catch (err) {
10602
- console.warn("[UpdateManager] check failed:", err);
10754
+ } catch (err2) {
10755
+ console.warn("[UpdateManager] check failed:", err2);
10603
10756
  return { hasUpdate: false };
10604
10757
  }
10605
10758
  }
@@ -10613,9 +10766,9 @@ var UpdateManager = class {
10613
10766
  });
10614
10767
  this.downloadedPath = filePath;
10615
10768
  return { success: true, filePath };
10616
- } catch (err) {
10617
- console.warn("[UpdateManager] download failed:", err);
10618
- return { success: false, error: String(err) };
10769
+ } catch (err2) {
10770
+ console.warn("[UpdateManager] download failed:", err2);
10771
+ return { success: false, error: String(err2) };
10619
10772
  }
10620
10773
  }
10621
10774
  install() {
@@ -10719,8 +10872,8 @@ function resolveModules(config) {
10719
10872
  }
10720
10873
  async function disposeContext(ctx) {
10721
10874
  await ctx.workspace.closeProject();
10722
- await ctx.registry.dispose().catch((err) => {
10723
- console.warn("[workbench] dispose registry encountered errors:", err);
10875
+ await ctx.registry.dispose().catch((err2) => {
10876
+ console.warn("[workbench] dispose registry encountered errors:", err2);
10724
10877
  });
10725
10878
  }
10726
10879
  function createConfiguredMainWindow(config, rendererDir2) {
@@ -10936,27 +11089,10 @@ async function createDevtoolsRuntime(config = {}) {
10936
11089
  appDataKeys: []
10937
11090
  };
10938
11091
  if (appId) {
10939
- try {
10940
- const info = await context.storageApi?.invoke(appId, "getStorageInfo", {});
10941
- if (info && typeof info === "object") {
10942
- const keys = info.keys;
10943
- if (Array.isArray(keys)) {
10944
- overview.storageKeys = keys.filter((key) => typeof key === "string");
10945
- overview.storageCount = keys.length;
10946
- }
10947
- }
10948
- } catch {
10949
- }
10950
- try {
10951
- const snapshot = context.appData?.snapshot?.(appId);
10952
- if (snapshot && typeof snapshot === "object") {
10953
- const entries = snapshot.entries;
10954
- if (entries && typeof entries === "object") {
10955
- overview.appDataKeys = Object.keys(entries);
10956
- }
10957
- }
10958
- } catch {
10959
- }
11092
+ const storage2 = await resolveNativeStorageOverview(context, appId);
11093
+ overview.storageKeys = storage2.storageKeys;
11094
+ overview.storageCount = storage2.storageCount;
11095
+ overview.appDataKeys = resolveNativeAppDataKeys(context, appId);
10960
11096
  }
10961
11097
  return overview;
10962
11098
  });
@@ -11084,7 +11220,7 @@ function launch(config = {}) {
11084
11220
  }
11085
11221
 
11086
11222
  // src/main/index.ts
11087
- launch().catch((err) => {
11088
- console.error("[devtools] fatal: launch() failed during boot", err);
11223
+ launch().catch((err2) => {
11224
+ console.error("[devtools] fatal: launch() failed during boot", err2);
11089
11225
  app16.exit(1);
11090
11226
  });