@dimina-kit/devtools 0.4.0-dev.20260702175315 → 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.
- package/dist/main/app/app.js +5 -25
- package/dist/main/app/native-overview.d.ts +27 -0
- package/dist/main/app/native-overview.js +44 -0
- package/dist/main/index.bundle.js +826 -690
- package/dist/main/ipc/bridge-router.js +180 -141
- package/dist/main/ipc/project-fs.js +39 -21
- package/dist/main/services/automation/handlers/app.js +143 -109
- package/dist/main/services/mcp/tools/simulator-tools.js +118 -97
- package/dist/main/services/network-forward/dispatch-batch.d.ts +22 -0
- package/dist/main/services/network-forward/dispatch-batch.js +21 -0
- package/dist/main/services/network-forward/index.js +5 -20
- package/dist/main/services/projects/create-project-service.js +54 -34
- package/dist/main/services/projects/types.d.ts +1 -32
- package/dist/main/services/service-console/console-api.js +36 -27
- package/dist/main/services/simulator-temp-files/disk.js +109 -86
- package/dist/main/services/workbench-coi-server.js +58 -45
- package/dist/main/utils/logger.d.ts +5 -12
- package/dist/main/utils/logger.js +5 -45
- package/dist/preload/instrumentation/wxml-extract.js +94 -62
- package/dist/preload/runtime/main-api-runner.d.ts +1 -1
- package/dist/preload/windows/main.cjs +284 -565
- package/dist/preload/windows/main.cjs.map +2 -2
- package/dist/preload/windows/simulator.cjs +37 -39
- package/dist/preload/windows/simulator.cjs.map +2 -2
- package/dist/preload/windows/simulator.js +37 -39
- package/dist/render-host/render-inspect.js +63 -52
- package/dist/renderer/assets/index-D-ksyN1p.js +49 -0
- package/dist/renderer/assets/workbenchSettings-COhuFF-i.js +8 -0
- package/dist/renderer/entries/main/index.html +1 -1
- package/dist/renderer/entries/workbench-settings/index.html +1 -1
- package/dist/shared/appdata-accumulator.js +53 -48
- package/dist/shared/open-in-editor-resource-path.d.ts +56 -0
- package/dist/shared/open-in-editor-resource-path.js +133 -0
- package/dist/shared/open-in-editor.d.ts +2 -9
- package/dist/shared/open-in-editor.js +8 -93
- package/dist/shared/types.d.ts +13 -0
- package/dist/simulator/assets/{device-shell-CUl0ILfn.js → device-shell-CiLAPa0I.js} +2 -2
- package/dist/simulator/assets/simulator-Dvnxry3y.js +10 -0
- package/dist/simulator/simulator.html +1 -1
- package/dist/vscode-workbench/assets/__vite-browser-external-CXi6Kz9W.js +1 -0
- package/dist/vscode-workbench/assets/{dist-CS7SQP3K.js → dist-TpGpmMGi.js} +3 -3
- package/dist/vscode-workbench/assets/{iconv-lite-umd-D3q-1-o6.js → iconv-lite-umd-C9tiCAJa.js} +1 -1
- package/dist/vscode-workbench/assets/{index-zjigpZ25.js → index-DJ1HyMZ7.js} +20 -22
- package/dist/vscode-workbench/assets/{jschardet-Cu4ufeHq.js → jschardet-DE1jV513.js} +1 -1
- package/dist/vscode-workbench/index.html +1 -1
- package/package.json +5 -5
- package/dist/renderer/assets/index-B-Dqb7G0.js +0 -49
- package/dist/renderer/assets/workbenchSettings-Bim9ol9R.js +0 -8
- package/dist/simulator/assets/simulator-Dz8iGcgy.js +0 -10
- 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
|
-
|
|
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,
|
|
216
|
-
if (
|
|
217
|
-
log.warn(`schema reject on '${channel}' at [${
|
|
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 (
|
|
221
|
-
log.error(`listener threw on '${channel}': ${
|
|
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}'`,
|
|
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 (
|
|
288
|
-
reportListenerError(channel,
|
|
289
|
-
const 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:
|
|
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((
|
|
275
|
+
Promise.resolve(ret).catch((err2) => reportListenerError(channel, err2));
|
|
311
276
|
}
|
|
312
|
-
} catch (
|
|
313
|
-
reportListenerError(channel,
|
|
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
|
|
559
|
-
|
|
560
|
-
return
|
|
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 (
|
|
604
|
-
if (
|
|
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 (
|
|
652
|
-
if (
|
|
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 (
|
|
718
|
-
if (
|
|
719
|
-
throw
|
|
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 (
|
|
737
|
-
if (
|
|
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 (
|
|
758
|
-
if (
|
|
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
|
-
|
|
811
|
-
|
|
812
|
-
|
|
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
|
-
|
|
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((
|
|
1154
|
-
console.warn("[safe-area] setSafeAreaInsetsOverride failed:",
|
|
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 (
|
|
1166
|
-
console.warn("[safe-area] debugger.attach failed; env(safe-area-inset-*) stays 0:",
|
|
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((
|
|
1407
|
-
console.error("[workbench] attachWorkbench \u2014 loadURL failed",
|
|
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((
|
|
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((
|
|
2268
|
+
}).catch((err2) => {
|
|
2247
2269
|
if (disposed) return;
|
|
2248
|
-
replyError(cmd,
|
|
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
|
|
2329
|
-
if (
|
|
2330
|
-
if ("
|
|
2331
|
-
if (
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
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 (
|
|
2631
|
-
console.warn("[devtools-host] point-at-service-wc failed; retry will rebuild:",
|
|
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 (
|
|
2826
|
-
console.error("[host-toolbar] onReady handler threw:",
|
|
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 (
|
|
3232
|
-
return { id: req.id, error:
|
|
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 (
|
|
3290
|
-
console.warn("[miniapp-partition] configurator failed for", partition,
|
|
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 (
|
|
3307
|
-
console.warn("[miniapp-partition] configurator failed for", partition,
|
|
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((
|
|
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((
|
|
3601
|
-
console.error("[workbench] attachNativeSimulator \u2014 loadURL failed",
|
|
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 (
|
|
3844
|
-
log2.warn("Failed to parse project.config.json",
|
|
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 (
|
|
3861
|
-
log2.warn("Failed to read project name from config",
|
|
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 (
|
|
4012
|
-
console.warn(`[workspace] ${label} failed (non-fatal):`,
|
|
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 (
|
|
4027
|
-
console.error("[workspace] session close failed:",
|
|
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: (
|
|
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 (
|
|
4102
|
+
} catch (err2) {
|
|
4076
4103
|
clearSimulatorServicewechatReferer();
|
|
4077
|
-
return { error: String(
|
|
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 (
|
|
4116
|
-
const error =
|
|
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
|
-
|
|
4611
|
-
const name = (
|
|
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
|
-
|
|
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
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
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
|
|
4632
|
-
|
|
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: ${
|
|
4664
|
+
throw new Error(`Template not found: ${id}`);
|
|
4635
4665
|
}
|
|
4636
4666
|
if (!template.source && !template.generate) {
|
|
4637
4667
|
throw new Error(
|
|
4638
|
-
`Template '${
|
|
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
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
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 (
|
|
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:
|
|
4778
|
+
detail: err2 instanceof Error ? err2.message : String(err2),
|
|
4735
4779
|
buttons: ["\u786E\u5B9A"]
|
|
4736
4780
|
});
|
|
4737
|
-
throw
|
|
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 (
|
|
4746
|
-
console.warn("[projects:create] failed to persist lastCreateBaseDir",
|
|
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((
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6917
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7180
|
-
|
|
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
|
-
|
|
7517
|
-
const
|
|
7518
|
-
const
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
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 (
|
|
7539
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
|
|
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", (
|
|
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 (
|
|
7979
|
-
response = { id, error: { message:
|
|
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
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
8557
|
-
|
|
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", (
|
|
8688
|
-
if (
|
|
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:",
|
|
8693
|
-
recordMcpFailed(
|
|
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
|
-
|
|
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((
|
|
8956
|
-
if (
|
|
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 (
|
|
9052
|
-
console.warn("[network-forward] debugger.attach failed; simulator network not captured:",
|
|
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((
|
|
9149
|
-
console.warn("[network-forward] Network.enable failed:",
|
|
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
|
|
9320
|
-
|
|
9321
|
-
|
|
9322
|
-
|
|
9323
|
-
|
|
9324
|
-
|
|
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
|
-
|
|
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
|
-
|
|
9809
|
-
|
|
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
|
-
|
|
9863
|
-
|
|
9864
|
-
|
|
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 (
|
|
10073
|
-
if (
|
|
10214
|
+
} catch (err2) {
|
|
10215
|
+
if (err2.code === "ENOENT") {
|
|
10074
10216
|
probe = path21.dirname(probe);
|
|
10075
10217
|
continue;
|
|
10076
10218
|
}
|
|
10077
|
-
throw
|
|
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, (
|
|
10334
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
10602
|
-
console.warn("[UpdateManager] check failed:",
|
|
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 (
|
|
10617
|
-
console.warn("[UpdateManager] download failed:",
|
|
10618
|
-
return { success: false, error: String(
|
|
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((
|
|
10723
|
-
console.warn("[workbench] dispose registry encountered errors:",
|
|
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
|
-
|
|
10940
|
-
|
|
10941
|
-
|
|
10942
|
-
|
|
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((
|
|
11088
|
-
console.error("[devtools] fatal: launch() failed during boot",
|
|
11223
|
+
launch().catch((err2) => {
|
|
11224
|
+
console.error("[devtools] fatal: launch() failed during boot", err2);
|
|
11089
11225
|
app16.exit(1);
|
|
11090
11226
|
});
|