@veolab/discoverylab 1.2.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mcp.json +2 -2
- package/README.md +182 -0
- package/dist/{chunk-TAODYZ52.js → chunk-3QRQEDWR.js} +510 -213
- package/dist/{chunk-L4SA5F5W.js → chunk-4L76GPRC.js} +1162 -58
- package/dist/chunk-6EGBXRDK.js +30 -0
- package/dist/{chunk-I6YD3QFM.js → chunk-FIL7IWEL.js} +5 -3
- package/dist/{chunk-4KLG6DDE.js → chunk-FNUN7EPB.js} +6 -6
- package/dist/chunk-GAKEFJ5T.js +481 -0
- package/dist/chunk-LB3RNE3O.js +109 -0
- package/dist/chunk-N6JJ2RGV.js +2680 -0
- package/dist/{chunk-XUKWS2CE.js → chunk-VRM42PML.js} +3546 -926
- package/dist/{chunk-TJ3H23LL.js → chunk-VVIOB362.js} +3 -1
- package/dist/{chunk-W3WJGYR6.js → chunk-XFVDP332.js} +8 -2
- package/dist/{chunk-QJXXHOV7.js → chunk-XKX6NBHF.js} +5 -1
- package/dist/cli.js +405 -11
- package/dist/{db-ADBEBNH6.js → db-6WLEVKUV.js} +3 -1
- package/dist/esvp-GSISVXLC.js +52 -0
- package/dist/esvp-mobile-GC7MAGMI.js +20 -0
- package/dist/index.d.ts +123 -1
- package/dist/index.html +11689 -8690
- package/dist/index.js +67 -11
- package/dist/{ocr-UTWC7537.js → ocr-QDYNCSPE.js} +1 -1
- package/dist/{playwright-R7Y5HREH.js → playwright-VZ7PXDC5.js} +2 -2
- package/dist/runtime/esvp-host-runtime/darwin-arm64/esvp-host-runtime +0 -0
- package/dist/runtime/esvp-host-runtime/manifest.json +10 -0
- package/dist/{server-3FBHBA7L.js → server-6N3KIEGP.js} +2 -1
- package/dist/server-FO3UVUZU.js +22 -0
- package/dist/{setup-27CQAX6K.js → setup-2SQC5UHJ.js} +4 -3
- package/dist/{tools-L6PKKQPY.js → tools-OCRMOQ4U.js} +63 -8
- package/package.json +36 -5
- package/dist/chunk-22OCFYHG.js +0 -6283
- package/dist/chunk-24VARQVO.js +0 -7818
- package/dist/chunk-2OGZX6C4.js +0 -588
- package/dist/chunk-2WCNIFRO.js +0 -6191
- package/dist/chunk-43U6UYV7.js +0 -590
- package/dist/chunk-4H2E3K2G.js +0 -7638
- package/dist/chunk-4MS6YW2B.js +0 -6490
- package/dist/chunk-4NNTRJOI.js +0 -7791
- package/dist/chunk-5F76VWME.js +0 -6397
- package/dist/chunk-5NEFN42O.js +0 -7791
- package/dist/chunk-63MEQ6UH.js +0 -7673
- package/dist/chunk-6H3NXFX3.js +0 -6861
- package/dist/chunk-7IDQLLBW.js +0 -311
- package/dist/chunk-7NP64TGJ.js +0 -6822
- package/dist/chunk-AATLY4KT.js +0 -6505
- package/dist/chunk-C7QUR7XX.js +0 -6397
- package/dist/chunk-CGKCE6MC.js +0 -6279
- package/dist/chunk-D25V6IWE.js +0 -6487
- package/dist/chunk-EQOZSXAT.js +0 -6822
- package/dist/chunk-FPHD7HSQ.js +0 -6812
- package/dist/chunk-GGJJUCFK.js +0 -7160
- package/dist/chunk-GLHOY3NN.js +0 -7805
- package/dist/chunk-GML5MKQA.js +0 -6398
- package/dist/chunk-GOL6FUJL.js +0 -6045
- package/dist/chunk-GSWHWEYC.js +0 -1346
- package/dist/chunk-HDKEQOF5.js +0 -7788
- package/dist/chunk-HZGSWVVS.js +0 -7111
- package/dist/chunk-IGZ5TICZ.js +0 -334
- package/dist/chunk-IRKQG33A.js +0 -7054
- package/dist/chunk-JFTBF4JR.js +0 -6040
- package/dist/chunk-JVLVBPUJ.js +0 -6180
- package/dist/chunk-JY3KC67R.js +0 -6504
- package/dist/chunk-KUFBCBNJ.js +0 -6815
- package/dist/chunk-KV7KDJ43.js +0 -7639
- package/dist/chunk-L5IJZV5F.js +0 -6822
- package/dist/chunk-MFFPQLU4.js +0 -7102
- package/dist/chunk-MJS2YKNR.js +0 -6397
- package/dist/chunk-MN6LCZHZ.js +0 -1320
- package/dist/chunk-NBAUZ7X2.js +0 -1336
- package/dist/chunk-NDBW6ELQ.js +0 -7638
- package/dist/chunk-O2HBSDI2.js +0 -6175
- package/dist/chunk-OFFIUYMG.js +0 -6341
- package/dist/chunk-OVCQGF2J.js +0 -1321
- package/dist/chunk-P4S7ZY6G.js +0 -7638
- package/dist/chunk-PBHUHSC3.js +0 -6002
- package/dist/chunk-PC4LR4ZI.js +0 -6359
- package/dist/chunk-PMTGGZ7R.js +0 -6397
- package/dist/chunk-PTXSB3UV.js +0 -497
- package/dist/chunk-PYUCY3U6.js +0 -1340
- package/dist/chunk-RDZDSOAL.js +0 -7750
- package/dist/chunk-RLW2OI2L.js +0 -6383
- package/dist/chunk-RUGHHO4K.js +0 -6395
- package/dist/chunk-SIOQVM2E.js +0 -6819
- package/dist/chunk-SR67SRIT.js +0 -1336
- package/dist/chunk-SSRXIO2V.js +0 -6822
- package/dist/chunk-SWSEKFON.js +0 -6487
- package/dist/chunk-TBG76CYG.js +0 -6395
- package/dist/chunk-V3CBINLD.js +0 -6812
- package/dist/chunk-VPYSLEGM.js +0 -6710
- package/dist/chunk-VY3BLXBW.js +0 -329
- package/dist/chunk-WTFOGVJQ.js +0 -6365
- package/dist/chunk-X64SFUT5.js +0 -6099
- package/dist/chunk-XIBF5LBD.js +0 -6395
- package/dist/chunk-Y5VDMSYC.js +0 -6701
- package/dist/chunk-YUBL36H4.js +0 -6605
- package/dist/chunk-YWVXFVSW.js +0 -6456
- package/dist/chunk-ZXZACOLD.js +0 -6822
- package/dist/db-IWIL65EX.js +0 -33
- package/dist/gridCompositor-ENKLFPWR.js +0 -409
- package/dist/playwright-A3OGSDRG.js +0 -38
- package/dist/playwright-ATDC4NYW.js +0 -38
- package/dist/playwright-E6EUFIJG.js +0 -38
- package/dist/server-2DXLKLFM.js +0 -13
- package/dist/server-2ICEWJVK.js +0 -13
- package/dist/server-2MQV3FNY.js +0 -13
- package/dist/server-2NGD7GE3.js +0 -13
- package/dist/server-2VKO76UK.js +0 -14
- package/dist/server-3BK2VFU7.js +0 -13
- package/dist/server-4LDOB3NX.js +0 -13
- package/dist/server-4YI44KDR.js +0 -13
- package/dist/server-64XMXA5P.js +0 -13
- package/dist/server-6IPHVUYT.js +0 -14
- package/dist/server-73ORHMJN.js +0 -13
- package/dist/server-73P7M3QB.js +0 -14
- package/dist/server-BPVRW5LJ.js +0 -14
- package/dist/server-BW4RKZIX.js +0 -13
- package/dist/server-CFS5SM5K.js +0 -13
- package/dist/server-DX7VYHHM.js +0 -13
- package/dist/server-F3YPX6ET.js +0 -13
- package/dist/server-FUXTR33I.js +0 -13
- package/dist/server-G2SY3DOS.js +0 -13
- package/dist/server-G32U7VOQ.js +0 -13
- package/dist/server-IOOZK4NP.js +0 -14
- package/dist/server-J52LMTBT.js +0 -13
- package/dist/server-JG7UKFGK.js +0 -14
- package/dist/server-JSCHEBOD.js +0 -13
- package/dist/server-K6KC4ZOM.js +0 -13
- package/dist/server-KJVRGWFE.js +0 -13
- package/dist/server-LCPB2L4U.js +0 -13
- package/dist/server-M7LDYKAJ.js +0 -13
- package/dist/server-MKVK6ZQQ.js +0 -13
- package/dist/server-MU52LCXT.js +0 -13
- package/dist/server-NM5CKDUU.js +0 -13
- package/dist/server-NPZN3FWO.js +0 -14
- package/dist/server-O5FIAHSY.js +0 -14
- package/dist/server-OESJUEYC.js +0 -13
- package/dist/server-ONSKQO4W.js +0 -13
- package/dist/server-P27BZXBL.js +0 -14
- package/dist/server-Q4FBWQUA.js +0 -13
- package/dist/server-RNQ7VUAL.js +0 -13
- package/dist/server-S6B5WUBT.js +0 -14
- package/dist/server-SRYNSGSP.js +0 -14
- package/dist/server-SUN3W2YK.js +0 -13
- package/dist/server-UA62LHZB.js +0 -13
- package/dist/server-UJB44EW5.js +0 -13
- package/dist/server-X3TLP6DX.js +0 -14
- package/dist/server-YT2UGEZK.js +0 -13
- package/dist/server-ZBPQ33V6.js +0 -14
- package/dist/setup-AQX4JQVR.js +0 -17
- package/dist/setup-EQTU7FI6.js +0 -17
- package/dist/tools-2KPB37GK.js +0 -178
- package/dist/tools-3H6IOWXV.js +0 -178
- package/dist/tools-3KYHPDCJ.js +0 -178
- package/dist/tools-75BAPCUM.js +0 -177
- package/dist/tools-BUVCUCRL.js +0 -178
- package/dist/tools-HDNODRS6.js +0 -178
- package/dist/tools-HP5MNY3D.js +0 -177
- package/dist/tools-N5N2IO7V.js +0 -178
- package/dist/tools-NFJEZ2FF.js +0 -177
- package/dist/tools-TLCKABUW.js +0 -178
|
@@ -1,15 +1,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createErrorResult,
|
|
3
|
+
createJsonResult,
|
|
4
|
+
createTextResult
|
|
5
|
+
} from "./chunk-XKX6NBHF.js";
|
|
1
6
|
import {
|
|
2
7
|
createLoginFlow,
|
|
3
8
|
createNavigationTestFlow,
|
|
4
9
|
createOnboardingFlow,
|
|
5
10
|
generateMaestroFlow,
|
|
11
|
+
getAdbCommand,
|
|
12
|
+
getAvailableTemplates,
|
|
6
13
|
getMaestroVersion,
|
|
7
14
|
isMaestroInstalled,
|
|
15
|
+
isTemplatesInstalled,
|
|
8
16
|
listMaestroDevices,
|
|
9
17
|
runMaestroTest,
|
|
10
18
|
runMaestroWithCapture,
|
|
11
19
|
startMaestroStudio
|
|
12
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-3QRQEDWR.js";
|
|
13
21
|
import {
|
|
14
22
|
createFormSubmissionScript,
|
|
15
23
|
createLoginScript,
|
|
@@ -22,13 +30,33 @@ import {
|
|
|
22
30
|
savePlaywrightScript,
|
|
23
31
|
showPlaywrightReport,
|
|
24
32
|
startPlaywrightCodegen
|
|
25
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-FIL7IWEL.js";
|
|
26
34
|
import {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
buildAppLabNetworkProfile
|
|
36
|
+
} from "./chunk-LB3RNE3O.js";
|
|
37
|
+
import {
|
|
38
|
+
attachESVPNetworkTrace,
|
|
39
|
+
captureESVPCheckpoint,
|
|
40
|
+
clearESVPNetwork,
|
|
41
|
+
configureESVPNetwork,
|
|
42
|
+
createESVPSession,
|
|
43
|
+
finishESVPSession,
|
|
44
|
+
getESVPArtifactContent,
|
|
45
|
+
getESVPConnection,
|
|
46
|
+
getESVPHealth,
|
|
47
|
+
getESVPReplayConsistency,
|
|
48
|
+
getESVPSession,
|
|
49
|
+
getESVPSessionNetwork,
|
|
50
|
+
getESVPTranscript,
|
|
51
|
+
inspectESVPSession,
|
|
52
|
+
listESVPArtifacts,
|
|
53
|
+
listESVPDevices,
|
|
54
|
+
listESVPSessions,
|
|
55
|
+
replayESVPSession,
|
|
56
|
+
runESVPActions,
|
|
57
|
+
runESVPPreflight,
|
|
58
|
+
validateESVPReplay
|
|
59
|
+
} from "./chunk-GAKEFJ5T.js";
|
|
32
60
|
import {
|
|
33
61
|
DATA_DIR,
|
|
34
62
|
EXPORTS_DIR,
|
|
@@ -39,12 +67,13 @@ import {
|
|
|
39
67
|
projectExports,
|
|
40
68
|
projects,
|
|
41
69
|
testVariables
|
|
42
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-VVIOB362.js";
|
|
43
71
|
import {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
72
|
+
analyzeText,
|
|
73
|
+
getAvailableOCREngines,
|
|
74
|
+
recognizeText,
|
|
75
|
+
recognizeTextBatch
|
|
76
|
+
} from "./chunk-XFVDP332.js";
|
|
48
77
|
|
|
49
78
|
// src/mcp/tools/ui.ts
|
|
50
79
|
import { z } from "zod";
|
|
@@ -337,17 +366,17 @@ async function captureMacScreenshot(outputPath, options) {
|
|
|
337
366
|
args.push("-R", `${x},${y},${width},${height}`);
|
|
338
367
|
}
|
|
339
368
|
args.push(outputPath);
|
|
340
|
-
return new Promise((
|
|
369
|
+
return new Promise((resolve2) => {
|
|
341
370
|
const proc = spawn("screencapture", args);
|
|
342
371
|
proc.on("close", (code) => {
|
|
343
372
|
if (code === 0 && existsSync(outputPath)) {
|
|
344
|
-
|
|
373
|
+
resolve2({ success: true, filePath: outputPath });
|
|
345
374
|
} else {
|
|
346
|
-
|
|
375
|
+
resolve2({ success: false, error: `screencapture exited with code ${code}` });
|
|
347
376
|
}
|
|
348
377
|
});
|
|
349
378
|
proc.on("error", (err) => {
|
|
350
|
-
|
|
379
|
+
resolve2({ success: false, error: err.message });
|
|
351
380
|
});
|
|
352
381
|
});
|
|
353
382
|
}
|
|
@@ -356,16 +385,16 @@ async function captureLinuxScreenshot(outputPath, options) {
|
|
|
356
385
|
if (options.windowId) {
|
|
357
386
|
args.push("-w");
|
|
358
387
|
}
|
|
359
|
-
return new Promise((
|
|
388
|
+
return new Promise((resolve2) => {
|
|
360
389
|
let proc = spawn("gnome-screenshot", args);
|
|
361
390
|
proc.on("error", () => {
|
|
362
391
|
proc = spawn("scrot", [outputPath]);
|
|
363
392
|
});
|
|
364
393
|
proc.on("close", (code) => {
|
|
365
394
|
if (code === 0 && existsSync(outputPath)) {
|
|
366
|
-
|
|
395
|
+
resolve2({ success: true, filePath: outputPath });
|
|
367
396
|
} else {
|
|
368
|
-
|
|
397
|
+
resolve2({ success: false, error: `Screenshot failed with code ${code}` });
|
|
369
398
|
}
|
|
370
399
|
});
|
|
371
400
|
});
|
|
@@ -380,17 +409,17 @@ async function captureWindowsScreenshot(outputPath, _options) {
|
|
|
380
409
|
$bitmap.Save('${outputPath.replace(/\\/g, "\\\\")}')
|
|
381
410
|
}
|
|
382
411
|
`;
|
|
383
|
-
return new Promise((
|
|
412
|
+
return new Promise((resolve2) => {
|
|
384
413
|
const proc = spawn("powershell", ["-Command", psScript]);
|
|
385
414
|
proc.on("close", (code) => {
|
|
386
415
|
if (code === 0 && existsSync(outputPath)) {
|
|
387
|
-
|
|
416
|
+
resolve2({ success: true, filePath: outputPath });
|
|
388
417
|
} else {
|
|
389
|
-
|
|
418
|
+
resolve2({ success: false, error: `PowerShell screenshot failed with code ${code}` });
|
|
390
419
|
}
|
|
391
420
|
});
|
|
392
421
|
proc.on("error", (err) => {
|
|
393
|
-
|
|
422
|
+
resolve2({ success: false, error: err.message });
|
|
394
423
|
});
|
|
395
424
|
});
|
|
396
425
|
}
|
|
@@ -497,7 +526,7 @@ async function stopRecording(sessionId) {
|
|
|
497
526
|
if (!session) {
|
|
498
527
|
return { success: false, error: `Recording session not found: ${sessionId}` };
|
|
499
528
|
}
|
|
500
|
-
return new Promise((
|
|
529
|
+
return new Promise((resolve2) => {
|
|
501
530
|
if (session.process.stdin) {
|
|
502
531
|
session.process.stdin.write("q");
|
|
503
532
|
session.process.stdin.end();
|
|
@@ -510,13 +539,13 @@ async function stopRecording(sessionId) {
|
|
|
510
539
|
activeRecordings.delete(sessionId);
|
|
511
540
|
const duration = (Date.now() - session.startTime.getTime()) / 1e3;
|
|
512
541
|
if (existsSync(session.outputPath)) {
|
|
513
|
-
|
|
542
|
+
resolve2({
|
|
514
543
|
success: true,
|
|
515
544
|
filePath: session.outputPath,
|
|
516
545
|
duration
|
|
517
546
|
});
|
|
518
547
|
} else {
|
|
519
|
-
|
|
548
|
+
resolve2({ success: false, error: "Recording file not found" });
|
|
520
549
|
}
|
|
521
550
|
});
|
|
522
551
|
});
|
|
@@ -645,7 +674,7 @@ async function captureIOSSimulatorScreenshot(options) {
|
|
|
645
674
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
646
675
|
const filename = `ios-screenshot-${timestamp}.png`;
|
|
647
676
|
const outputPath = join2(projectDir, filename);
|
|
648
|
-
return new Promise((
|
|
677
|
+
return new Promise((resolve2) => {
|
|
649
678
|
const args = ["simctl", "io", options.deviceId, "screenshot", outputPath];
|
|
650
679
|
const proc = spawn2("xcrun", args);
|
|
651
680
|
let stderr = "";
|
|
@@ -654,13 +683,13 @@ async function captureIOSSimulatorScreenshot(options) {
|
|
|
654
683
|
});
|
|
655
684
|
proc.on("close", (code) => {
|
|
656
685
|
if (code === 0 && existsSync2(outputPath)) {
|
|
657
|
-
|
|
686
|
+
resolve2({ success: true, filePath: outputPath });
|
|
658
687
|
} else {
|
|
659
|
-
|
|
688
|
+
resolve2({ success: false, error: stderr || `xcrun simctl failed with code ${code}` });
|
|
660
689
|
}
|
|
661
690
|
});
|
|
662
691
|
proc.on("error", (err) => {
|
|
663
|
-
|
|
692
|
+
resolve2({ success: false, error: err.message });
|
|
664
693
|
});
|
|
665
694
|
});
|
|
666
695
|
}
|
|
@@ -692,7 +721,7 @@ async function stopIOSSimulatorRecording(sessionId) {
|
|
|
692
721
|
if (!session) {
|
|
693
722
|
return { success: false, error: `Recording session not found: ${sessionId}` };
|
|
694
723
|
}
|
|
695
|
-
return new Promise((
|
|
724
|
+
return new Promise((resolve2) => {
|
|
696
725
|
session.process.kill("SIGINT");
|
|
697
726
|
const timeout = setTimeout(() => {
|
|
698
727
|
session.process.kill("SIGKILL");
|
|
@@ -701,16 +730,30 @@ async function stopIOSSimulatorRecording(sessionId) {
|
|
|
701
730
|
clearTimeout(timeout);
|
|
702
731
|
iosRecordingSessions.delete(sessionId);
|
|
703
732
|
if (existsSync2(session.outputPath)) {
|
|
704
|
-
|
|
733
|
+
resolve2({ success: true, filePath: session.outputPath });
|
|
705
734
|
} else {
|
|
706
|
-
|
|
735
|
+
resolve2({ success: false, error: "Recording file not found" });
|
|
707
736
|
}
|
|
708
737
|
});
|
|
709
738
|
});
|
|
710
739
|
}
|
|
740
|
+
function quoteCommand(cmd) {
|
|
741
|
+
return cmd.includes(" ") ? `"${cmd}"` : cmd;
|
|
742
|
+
}
|
|
743
|
+
function getAdbCommandOrThrow() {
|
|
744
|
+
const adbCommand = getAdbCommand();
|
|
745
|
+
if (!adbCommand) {
|
|
746
|
+
throw new Error("ADB not found. Set ANDROID_HOME/ANDROID_SDK_ROOT or add adb to PATH.");
|
|
747
|
+
}
|
|
748
|
+
return adbCommand;
|
|
749
|
+
}
|
|
711
750
|
function listAndroidEmulators() {
|
|
712
751
|
try {
|
|
713
|
-
const
|
|
752
|
+
const adbCommand = getAdbCommand();
|
|
753
|
+
if (!adbCommand) {
|
|
754
|
+
return [];
|
|
755
|
+
}
|
|
756
|
+
const output = execSync2(`${quoteCommand(adbCommand)} devices -l`, { encoding: "utf-8" });
|
|
714
757
|
const lines = output.split("\n").slice(1);
|
|
715
758
|
const devices3 = [];
|
|
716
759
|
for (const line of lines) {
|
|
@@ -747,28 +790,35 @@ async function captureAndroidEmulatorScreenshot(options) {
|
|
|
747
790
|
const filename = `android-screenshot-${timestamp}.png`;
|
|
748
791
|
const outputPath = join2(projectDir, filename);
|
|
749
792
|
const tempPath = "/sdcard/screenshot.png";
|
|
750
|
-
return new Promise((
|
|
751
|
-
|
|
793
|
+
return new Promise((resolve2) => {
|
|
794
|
+
let adbCommand;
|
|
795
|
+
try {
|
|
796
|
+
adbCommand = getAdbCommandOrThrow();
|
|
797
|
+
} catch (error) {
|
|
798
|
+
resolve2({ success: false, error: error instanceof Error ? error.message : String(error) });
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
const captureProc = spawn2(adbCommand, ["-s", options.deviceId, "shell", "screencap", "-p", tempPath]);
|
|
752
802
|
captureProc.on("close", (captureCode) => {
|
|
753
803
|
if (captureCode !== 0) {
|
|
754
|
-
|
|
804
|
+
resolve2({ success: false, error: `screencap failed with code ${captureCode}` });
|
|
755
805
|
return;
|
|
756
806
|
}
|
|
757
|
-
const pullProc = spawn2(
|
|
807
|
+
const pullProc = spawn2(adbCommand, ["-s", options.deviceId, "pull", tempPath, outputPath]);
|
|
758
808
|
pullProc.on("close", (pullCode) => {
|
|
759
|
-
spawn2(
|
|
809
|
+
spawn2(adbCommand, ["-s", options.deviceId, "shell", "rm", tempPath]);
|
|
760
810
|
if (pullCode === 0 && existsSync2(outputPath)) {
|
|
761
|
-
|
|
811
|
+
resolve2({ success: true, filePath: outputPath });
|
|
762
812
|
} else {
|
|
763
|
-
|
|
813
|
+
resolve2({ success: false, error: `adb pull failed with code ${pullCode}` });
|
|
764
814
|
}
|
|
765
815
|
});
|
|
766
816
|
pullProc.on("error", (err) => {
|
|
767
|
-
|
|
817
|
+
resolve2({ success: false, error: err.message });
|
|
768
818
|
});
|
|
769
819
|
});
|
|
770
820
|
captureProc.on("error", (err) => {
|
|
771
|
-
|
|
821
|
+
resolve2({ success: false, error: err.message });
|
|
772
822
|
});
|
|
773
823
|
});
|
|
774
824
|
}
|
|
@@ -782,14 +832,21 @@ async function startAndroidEmulatorRecording(options) {
|
|
|
782
832
|
const filename = `android-recording-${timestamp}.mp4`;
|
|
783
833
|
const outputPath = join2(projectDir, filename);
|
|
784
834
|
const tempPath = "/sdcard/recording.mp4";
|
|
835
|
+
let adbCommand;
|
|
836
|
+
try {
|
|
837
|
+
adbCommand = getAdbCommandOrThrow();
|
|
838
|
+
} catch (error) {
|
|
839
|
+
return { error: error instanceof Error ? error.message : String(error) };
|
|
840
|
+
}
|
|
785
841
|
const args = ["-s", options.deviceId, "shell", "screenrecord"];
|
|
786
842
|
if (options.duration) {
|
|
787
843
|
args.push("--time-limit", Math.min(options.duration, 180).toString());
|
|
788
844
|
}
|
|
789
845
|
args.push(tempPath);
|
|
790
|
-
const proc = spawn2(
|
|
846
|
+
const proc = spawn2(adbCommand, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
791
847
|
androidRecordingSessions.set(sessionId, {
|
|
792
848
|
process: proc,
|
|
849
|
+
adbCommand,
|
|
793
850
|
deviceId: options.deviceId,
|
|
794
851
|
tempPath,
|
|
795
852
|
outputPath
|
|
@@ -802,22 +859,22 @@ async function stopAndroidEmulatorRecording(sessionId) {
|
|
|
802
859
|
if (!session) {
|
|
803
860
|
return { success: false, error: `Recording session not found: ${sessionId}` };
|
|
804
861
|
}
|
|
805
|
-
return new Promise((
|
|
862
|
+
return new Promise((resolve2) => {
|
|
806
863
|
session.process.kill("SIGINT");
|
|
807
864
|
setTimeout(() => {
|
|
808
|
-
const pullProc = spawn2(
|
|
865
|
+
const pullProc = spawn2(session.adbCommand, ["-s", session.deviceId, "pull", session.tempPath, session.outputPath]);
|
|
809
866
|
pullProc.on("close", (pullCode) => {
|
|
810
|
-
spawn2(
|
|
867
|
+
spawn2(session.adbCommand, ["-s", session.deviceId, "shell", "rm", session.tempPath]);
|
|
811
868
|
androidRecordingSessions.delete(sessionId);
|
|
812
869
|
if (pullCode === 0 && existsSync2(session.outputPath)) {
|
|
813
|
-
|
|
870
|
+
resolve2({ success: true, filePath: session.outputPath });
|
|
814
871
|
} else {
|
|
815
|
-
|
|
872
|
+
resolve2({ success: false, error: "Failed to pull recording file" });
|
|
816
873
|
}
|
|
817
874
|
});
|
|
818
875
|
pullProc.on("error", (err) => {
|
|
819
876
|
androidRecordingSessions.delete(sessionId);
|
|
820
|
-
|
|
877
|
+
resolve2({ success: false, error: err.message });
|
|
821
878
|
});
|
|
822
879
|
}, 1e3);
|
|
823
880
|
});
|
|
@@ -875,7 +932,9 @@ async function stopEmulatorRecording(sessionId, platformHint) {
|
|
|
875
932
|
}
|
|
876
933
|
function checkADB() {
|
|
877
934
|
try {
|
|
878
|
-
|
|
935
|
+
const adbCommand = getAdbCommand();
|
|
936
|
+
if (!adbCommand) return false;
|
|
937
|
+
execSync2(`${quoteCommand(adbCommand)} version`, { stdio: "ignore" });
|
|
879
938
|
return true;
|
|
880
939
|
} catch {
|
|
881
940
|
return false;
|
|
@@ -1197,7 +1256,7 @@ async function extractFrames(options) {
|
|
|
1197
1256
|
return { success: false, error: "Failed to read video information" };
|
|
1198
1257
|
}
|
|
1199
1258
|
const args = buildFFmpegArgs(options, framesDir, videoInfo, outputFormat);
|
|
1200
|
-
return new Promise((
|
|
1259
|
+
return new Promise((resolve2) => {
|
|
1201
1260
|
const proc = spawn3("ffmpeg", args);
|
|
1202
1261
|
let stderr = "";
|
|
1203
1262
|
proc.stderr.on("data", (data) => {
|
|
@@ -1205,11 +1264,11 @@ async function extractFrames(options) {
|
|
|
1205
1264
|
});
|
|
1206
1265
|
proc.on("close", (code) => {
|
|
1207
1266
|
if (code !== 0) {
|
|
1208
|
-
|
|
1267
|
+
resolve2({ success: false, error: `FFmpeg failed: ${stderr.slice(-500)}` });
|
|
1209
1268
|
return;
|
|
1210
1269
|
}
|
|
1211
1270
|
const frames2 = listExtractedFrames(framesDir, videoInfo.fps, options.fps || 1);
|
|
1212
|
-
|
|
1271
|
+
resolve2({
|
|
1213
1272
|
success: true,
|
|
1214
1273
|
framesDir,
|
|
1215
1274
|
frameCount: frames2.length,
|
|
@@ -1217,7 +1276,7 @@ async function extractFrames(options) {
|
|
|
1217
1276
|
});
|
|
1218
1277
|
});
|
|
1219
1278
|
proc.on("error", (err) => {
|
|
1220
|
-
|
|
1279
|
+
resolve2({ success: false, error: err.message });
|
|
1221
1280
|
});
|
|
1222
1281
|
});
|
|
1223
1282
|
}
|
|
@@ -2959,7 +3018,7 @@ function buildOverlayFilter(overlay) {
|
|
|
2959
3018
|
return null;
|
|
2960
3019
|
}
|
|
2961
3020
|
async function runFFmpegWithProgress(args, onProgress) {
|
|
2962
|
-
return new Promise((
|
|
3021
|
+
return new Promise((resolve2, reject) => {
|
|
2963
3022
|
const ffmpeg = spawn4("ffmpeg", args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
2964
3023
|
ffmpeg.stderr?.on("data", (data) => {
|
|
2965
3024
|
const output = data.toString();
|
|
@@ -2981,7 +3040,7 @@ async function runFFmpegWithProgress(args, onProgress) {
|
|
|
2981
3040
|
});
|
|
2982
3041
|
ffmpeg.on("close", (code) => {
|
|
2983
3042
|
if (code === 0) {
|
|
2984
|
-
|
|
3043
|
+
resolve2();
|
|
2985
3044
|
} else {
|
|
2986
3045
|
reject(new Error(`FFmpeg exited with code ${code}`));
|
|
2987
3046
|
}
|
|
@@ -6314,6 +6373,1025 @@ var taskHubTools = [
|
|
|
6314
6373
|
taskHubTestMapToggleTool
|
|
6315
6374
|
];
|
|
6316
6375
|
|
|
6376
|
+
// src/mcp/tools/esvp.ts
|
|
6377
|
+
import { readFile } from "fs/promises";
|
|
6378
|
+
import { join as join13, resolve } from "path";
|
|
6379
|
+
import { z as z10 } from "zod";
|
|
6380
|
+
var jsonObjectSchema = z10.record(z10.string(), z10.any());
|
|
6381
|
+
var actionSchema = z10.object({
|
|
6382
|
+
name: z10.string().min(1).describe("ESVP action name"),
|
|
6383
|
+
args: jsonObjectSchema.optional().describe("Action arguments"),
|
|
6384
|
+
checkpointAfter: z10.boolean().optional(),
|
|
6385
|
+
checkpointLabel: z10.string().optional()
|
|
6386
|
+
});
|
|
6387
|
+
var crashClipSchema = z10.object({
|
|
6388
|
+
enabled: z10.boolean().optional(),
|
|
6389
|
+
pre_seconds: z10.number().optional(),
|
|
6390
|
+
post_seconds: z10.number().optional(),
|
|
6391
|
+
chunk_seconds: z10.number().optional()
|
|
6392
|
+
});
|
|
6393
|
+
var proxySchema = z10.object({
|
|
6394
|
+
host: z10.string(),
|
|
6395
|
+
port: z10.number(),
|
|
6396
|
+
protocol: z10.string().optional(),
|
|
6397
|
+
bypass: z10.array(z10.string()).optional()
|
|
6398
|
+
});
|
|
6399
|
+
var projectRecordingSchema = z10.object({
|
|
6400
|
+
recordingId: z10.string().min(1).describe("Mobile recording / project ID inside ~/.discoverylab/projects/maestro-recordings")
|
|
6401
|
+
});
|
|
6402
|
+
var projectValidationProfileIdSchema = z10.enum([
|
|
6403
|
+
"standard",
|
|
6404
|
+
"app-http-trace",
|
|
6405
|
+
"mitm-simulator",
|
|
6406
|
+
"inject-503",
|
|
6407
|
+
"timeout",
|
|
6408
|
+
"delay-1200"
|
|
6409
|
+
]);
|
|
6410
|
+
var projectValidationNetworkSchema = z10.object({
|
|
6411
|
+
enabled: z10.boolean().optional(),
|
|
6412
|
+
mode: z10.enum(["managed-proxy", "external-proxy", "external-mitm", "app-http-trace"]).optional(),
|
|
6413
|
+
profile: z10.string().optional(),
|
|
6414
|
+
label: z10.string().optional(),
|
|
6415
|
+
connectivity: z10.enum(["online", "offline", "reset"]).optional(),
|
|
6416
|
+
proxy: jsonObjectSchema.optional(),
|
|
6417
|
+
capture: jsonObjectSchema.optional(),
|
|
6418
|
+
faults: jsonObjectSchema.optional()
|
|
6419
|
+
}).nullable();
|
|
6420
|
+
function getMobileRecordingDir(recordingId) {
|
|
6421
|
+
return join13(PROJECTS_DIR, "maestro-recordings", recordingId);
|
|
6422
|
+
}
|
|
6423
|
+
function getMobileRecordingSessionPath(recordingId) {
|
|
6424
|
+
return join13(getMobileRecordingDir(recordingId), "session.json");
|
|
6425
|
+
}
|
|
6426
|
+
async function readMobileRecordingSession(recordingId) {
|
|
6427
|
+
const sessionPath = getMobileRecordingSessionPath(recordingId);
|
|
6428
|
+
const raw = await readFile(sessionPath, "utf8").catch(() => null);
|
|
6429
|
+
if (!raw) {
|
|
6430
|
+
throw new Error(`Recording not found: ${recordingId}`);
|
|
6431
|
+
}
|
|
6432
|
+
return JSON.parse(raw);
|
|
6433
|
+
}
|
|
6434
|
+
function resolveProjectRecordingESVPState(session) {
|
|
6435
|
+
return session?.esvp && typeof session.esvp === "object" ? session.esvp : null;
|
|
6436
|
+
}
|
|
6437
|
+
function resolveProjectRecordingESVPSourceSessionId(session) {
|
|
6438
|
+
const esvp = resolveProjectRecordingESVPState(session);
|
|
6439
|
+
if (!esvp) return null;
|
|
6440
|
+
const validation = esvp.validation && typeof esvp.validation === "object" ? esvp.validation : null;
|
|
6441
|
+
const network = esvp.network && typeof esvp.network === "object" ? esvp.network : null;
|
|
6442
|
+
const validationId = typeof validation?.sourceSessionId === "string" ? validation.sourceSessionId.trim() : "";
|
|
6443
|
+
const networkId = typeof network?.sourceSessionId === "string" ? network.sourceSessionId.trim() : "";
|
|
6444
|
+
const directId = typeof esvp.currentSessionId === "string" ? esvp.currentSessionId.trim() : "";
|
|
6445
|
+
return validationId || networkId || directId || null;
|
|
6446
|
+
}
|
|
6447
|
+
function resolveProjectRecordingReplaySessionId(session) {
|
|
6448
|
+
const esvp = resolveProjectRecordingESVPState(session);
|
|
6449
|
+
const validation = esvp?.validation && typeof esvp.validation === "object" ? esvp.validation : null;
|
|
6450
|
+
const replayId = typeof validation?.replaySessionId === "string" ? validation.replaySessionId.trim() : "";
|
|
6451
|
+
return replayId || null;
|
|
6452
|
+
}
|
|
6453
|
+
function resolveProjectRecordingESVPServerUrl(session) {
|
|
6454
|
+
const esvp = resolveProjectRecordingESVPState(session);
|
|
6455
|
+
if (!esvp || typeof esvp.serverUrl !== "string") return void 0;
|
|
6456
|
+
const serverUrl = esvp.serverUrl.trim();
|
|
6457
|
+
return serverUrl || void 0;
|
|
6458
|
+
}
|
|
6459
|
+
function resolveAppLabBaseUrl(appLabUrl) {
|
|
6460
|
+
const raw = String(appLabUrl || process.env.DISCOVERYLAB_APP_URL || "http://127.0.0.1:3847").trim();
|
|
6461
|
+
return raw.replace(/\/+$/, "");
|
|
6462
|
+
}
|
|
6463
|
+
async function callAppLabJson(path10, init = {}, appLabUrl) {
|
|
6464
|
+
const baseUrl = resolveAppLabBaseUrl(appLabUrl);
|
|
6465
|
+
const targetUrl = `${baseUrl}${path10.startsWith("/") ? path10 : `/${path10}`}`;
|
|
6466
|
+
const response = await fetch(targetUrl, init).catch((error) => {
|
|
6467
|
+
throw new Error(`Failed to reach App Lab at ${baseUrl}. Start the local App Lab server before using project-scoped ESVP tools. ${error instanceof Error ? error.message : String(error)}`);
|
|
6468
|
+
});
|
|
6469
|
+
const text = await response.text();
|
|
6470
|
+
let payload = null;
|
|
6471
|
+
if (text.trim()) {
|
|
6472
|
+
try {
|
|
6473
|
+
payload = JSON.parse(text);
|
|
6474
|
+
} catch {
|
|
6475
|
+
payload = { raw: text };
|
|
6476
|
+
}
|
|
6477
|
+
}
|
|
6478
|
+
if (!response.ok) {
|
|
6479
|
+
const message = payload?.error || payload?.message || `App Lab request failed (${response.status} ${response.statusText})`;
|
|
6480
|
+
throw new Error(String(message));
|
|
6481
|
+
}
|
|
6482
|
+
return {
|
|
6483
|
+
appLabUrl: baseUrl,
|
|
6484
|
+
payload
|
|
6485
|
+
};
|
|
6486
|
+
}
|
|
6487
|
+
function buildProjectValidationNetwork(profileId, network) {
|
|
6488
|
+
if (network) return network;
|
|
6489
|
+
switch (profileId) {
|
|
6490
|
+
case "app-http-trace":
|
|
6491
|
+
return {
|
|
6492
|
+
enabled: true,
|
|
6493
|
+
profile: "applab-app-http-trace",
|
|
6494
|
+
label: "App Lab App HTTP Trace",
|
|
6495
|
+
mode: "app-http-trace"
|
|
6496
|
+
};
|
|
6497
|
+
case "mitm-simulator":
|
|
6498
|
+
return {
|
|
6499
|
+
enabled: true,
|
|
6500
|
+
profile: "applab-mitm-beta",
|
|
6501
|
+
label: "App Lab MITM Beta",
|
|
6502
|
+
mode: "external-mitm"
|
|
6503
|
+
};
|
|
6504
|
+
case "inject-503":
|
|
6505
|
+
return {
|
|
6506
|
+
enabled: true,
|
|
6507
|
+
profile: "applab-inject-503",
|
|
6508
|
+
label: "App Lab Inject 503",
|
|
6509
|
+
mode: "managed-proxy",
|
|
6510
|
+
faults: {
|
|
6511
|
+
status_code: 503,
|
|
6512
|
+
body_patch: {
|
|
6513
|
+
error: "Injected by ESVP"
|
|
6514
|
+
}
|
|
6515
|
+
}
|
|
6516
|
+
};
|
|
6517
|
+
case "timeout":
|
|
6518
|
+
return {
|
|
6519
|
+
enabled: true,
|
|
6520
|
+
profile: "applab-timeout",
|
|
6521
|
+
label: "App Lab Timeout",
|
|
6522
|
+
mode: "managed-proxy",
|
|
6523
|
+
faults: {
|
|
6524
|
+
timeout: true
|
|
6525
|
+
}
|
|
6526
|
+
};
|
|
6527
|
+
case "delay-1200":
|
|
6528
|
+
return {
|
|
6529
|
+
enabled: true,
|
|
6530
|
+
profile: "applab-delay-1200",
|
|
6531
|
+
label: "App Lab Delay 1200ms",
|
|
6532
|
+
mode: "managed-proxy",
|
|
6533
|
+
faults: {
|
|
6534
|
+
delay_ms: 1200
|
|
6535
|
+
}
|
|
6536
|
+
};
|
|
6537
|
+
case "standard":
|
|
6538
|
+
default:
|
|
6539
|
+
return {
|
|
6540
|
+
enabled: true,
|
|
6541
|
+
profile: "applab-standard",
|
|
6542
|
+
label: "App Lab Standard Capture",
|
|
6543
|
+
mode: "external-proxy"
|
|
6544
|
+
};
|
|
6545
|
+
}
|
|
6546
|
+
}
|
|
6547
|
+
function resolveDefaultDeviceId(executor) {
|
|
6548
|
+
if (executor === "fake") return void 0;
|
|
6549
|
+
const wantedPlatform = executor === "ios-sim" || executor === "maestro-ios" ? "ios" : "android";
|
|
6550
|
+
const booted = listAllEmulators().find(
|
|
6551
|
+
(device) => device.platform === wantedPlatform && device.state === "booted"
|
|
6552
|
+
);
|
|
6553
|
+
return booted?.id;
|
|
6554
|
+
}
|
|
6555
|
+
function executorToPlatform(executor) {
|
|
6556
|
+
if (executor === "ios-sim" || executor === "maestro-ios") return "ios";
|
|
6557
|
+
if (executor === "adb") return "android";
|
|
6558
|
+
return void 0;
|
|
6559
|
+
}
|
|
6560
|
+
async function makeBaseResult(serverUrl) {
|
|
6561
|
+
const connection = await getESVPConnection(serverUrl);
|
|
6562
|
+
return {
|
|
6563
|
+
serverUrl: connection.serverUrl,
|
|
6564
|
+
connectionMode: connection.mode
|
|
6565
|
+
};
|
|
6566
|
+
}
|
|
6567
|
+
function normalizeActions(actions) {
|
|
6568
|
+
return actions.map((action) => ({
|
|
6569
|
+
name: action.name,
|
|
6570
|
+
...action.args ? { args: action.args } : {},
|
|
6571
|
+
...action.checkpointAfter !== void 0 ? { checkpointAfter: action.checkpointAfter } : {},
|
|
6572
|
+
...action.checkpointLabel ? { checkpointLabel: action.checkpointLabel } : {}
|
|
6573
|
+
}));
|
|
6574
|
+
}
|
|
6575
|
+
async function resolveTracePayload(params) {
|
|
6576
|
+
if (params.payload !== void 0) return params.payload;
|
|
6577
|
+
if (!params.traceFilePath) {
|
|
6578
|
+
throw new Error("payload ou traceFilePath \xE9 obrigat\xF3rio");
|
|
6579
|
+
}
|
|
6580
|
+
const absPath = params.traceFilePath.startsWith("/") ? params.traceFilePath : resolve(process.cwd(), params.traceFilePath);
|
|
6581
|
+
const raw = await readFile(absPath, "utf8");
|
|
6582
|
+
try {
|
|
6583
|
+
return JSON.parse(raw);
|
|
6584
|
+
} catch {
|
|
6585
|
+
return raw;
|
|
6586
|
+
}
|
|
6587
|
+
}
|
|
6588
|
+
var esvpStatusTool = {
|
|
6589
|
+
name: "dlab.esvp.status",
|
|
6590
|
+
description: "Check if an ESVP control-plane is reachable and return its health payload.",
|
|
6591
|
+
inputSchema: z10.object({
|
|
6592
|
+
serverUrl: z10.string().url().optional().describe("ESVP control-plane base URL")
|
|
6593
|
+
}),
|
|
6594
|
+
handler: async (params) => {
|
|
6595
|
+
try {
|
|
6596
|
+
const health = await getESVPHealth(params.serverUrl);
|
|
6597
|
+
return createJsonResult({
|
|
6598
|
+
...await makeBaseResult(params.serverUrl),
|
|
6599
|
+
health
|
|
6600
|
+
});
|
|
6601
|
+
} catch (error) {
|
|
6602
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
6603
|
+
}
|
|
6604
|
+
}
|
|
6605
|
+
};
|
|
6606
|
+
var esvpDevicesTool = {
|
|
6607
|
+
name: "dlab.esvp.devices",
|
|
6608
|
+
description: "List ESVP-visible ADB devices and/or iOS Simulators from the public control-plane.",
|
|
6609
|
+
inputSchema: z10.object({
|
|
6610
|
+
serverUrl: z10.string().url().optional().describe("ESVP control-plane base URL"),
|
|
6611
|
+
platform: z10.enum(["adb", "ios-sim", "maestro-ios", "all"]).optional().describe("Which device family to query")
|
|
6612
|
+
}),
|
|
6613
|
+
handler: async (params) => {
|
|
6614
|
+
try {
|
|
6615
|
+
const devices3 = await listESVPDevices(params.platform || "all", params.serverUrl);
|
|
6616
|
+
return createJsonResult({
|
|
6617
|
+
...await makeBaseResult(params.serverUrl),
|
|
6618
|
+
devices: devices3
|
|
6619
|
+
});
|
|
6620
|
+
} catch (error) {
|
|
6621
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
6622
|
+
}
|
|
6623
|
+
}
|
|
6624
|
+
};
|
|
6625
|
+
var esvpSessionsListTool = {
|
|
6626
|
+
name: "dlab.esvp.sessions.list",
|
|
6627
|
+
description: "List public ESVP sessions from the configured server.",
|
|
6628
|
+
inputSchema: z10.object({
|
|
6629
|
+
serverUrl: z10.string().url().optional().describe("ESVP control-plane base URL")
|
|
6630
|
+
}),
|
|
6631
|
+
handler: async (params) => {
|
|
6632
|
+
try {
|
|
6633
|
+
const sessions = await listESVPSessions(params.serverUrl);
|
|
6634
|
+
return createJsonResult({
|
|
6635
|
+
...await makeBaseResult(params.serverUrl),
|
|
6636
|
+
...sessions
|
|
6637
|
+
});
|
|
6638
|
+
} catch (error) {
|
|
6639
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
6640
|
+
}
|
|
6641
|
+
}
|
|
6642
|
+
};
|
|
6643
|
+
var esvpSessionCreateTool = {
|
|
6644
|
+
name: "dlab.esvp.session.create",
|
|
6645
|
+
description: "Create an ESVP session using the public contract. Auto-selects a booted emulator/simulator when possible. Use withNetwork to auto-configure the default App Lab external-proxy profile.",
|
|
6646
|
+
inputSchema: z10.object({
|
|
6647
|
+
serverUrl: z10.string().url().optional().describe("ESVP control-plane base URL"),
|
|
6648
|
+
executor: z10.enum(["fake", "adb", "ios-sim", "maestro-ios"]).describe("Public ESVP executor"),
|
|
6649
|
+
deviceId: z10.string().optional().describe("Device ID. Optional for fake and for booted local emulators/simulators"),
|
|
6650
|
+
meta: jsonObjectSchema.optional().describe("Session metadata"),
|
|
6651
|
+
crashClip: crashClipSchema.optional().describe("Optional crash clip config"),
|
|
6652
|
+
withNetwork: z10.boolean().optional().describe("Auto-configure the default App Lab external-proxy profile after creating the session")
|
|
6653
|
+
}),
|
|
6654
|
+
handler: async (params) => {
|
|
6655
|
+
try {
|
|
6656
|
+
const resolvedDeviceId = params.deviceId || resolveDefaultDeviceId(params.executor);
|
|
6657
|
+
const response = await createESVPSession(
|
|
6658
|
+
{
|
|
6659
|
+
executor: params.executor,
|
|
6660
|
+
...resolvedDeviceId ? { deviceId: resolvedDeviceId } : {},
|
|
6661
|
+
meta: {
|
|
6662
|
+
source: "applab-discovery",
|
|
6663
|
+
...params.meta || {}
|
|
6664
|
+
},
|
|
6665
|
+
...params.crashClip ? {
|
|
6666
|
+
crash_clip: params.crashClip
|
|
6667
|
+
} : {}
|
|
6668
|
+
},
|
|
6669
|
+
params.serverUrl
|
|
6670
|
+
);
|
|
6671
|
+
let networkConfigured = null;
|
|
6672
|
+
if (params.withNetwork) {
|
|
6673
|
+
const sessionId = String(response?.session?.id || response?.id || "");
|
|
6674
|
+
if (sessionId) {
|
|
6675
|
+
networkConfigured = await configureESVPNetwork(
|
|
6676
|
+
sessionId,
|
|
6677
|
+
buildAppLabNetworkProfile(
|
|
6678
|
+
{
|
|
6679
|
+
enabled: true,
|
|
6680
|
+
mode: "external-proxy",
|
|
6681
|
+
profile: "applab-standard-capture",
|
|
6682
|
+
label: "App Lab Standard Capture"
|
|
6683
|
+
},
|
|
6684
|
+
{
|
|
6685
|
+
platform: executorToPlatform(params.executor),
|
|
6686
|
+
deviceId: resolvedDeviceId
|
|
6687
|
+
}
|
|
6688
|
+
) || {},
|
|
6689
|
+
params.serverUrl
|
|
6690
|
+
).catch((err) => ({ error: err instanceof Error ? err.message : String(err) }));
|
|
6691
|
+
}
|
|
6692
|
+
}
|
|
6693
|
+
return createJsonResult({
|
|
6694
|
+
...await makeBaseResult(params.serverUrl),
|
|
6695
|
+
autoSelectedDeviceId: resolvedDeviceId || null,
|
|
6696
|
+
...response,
|
|
6697
|
+
...networkConfigured ? { networkConfigured } : {}
|
|
6698
|
+
});
|
|
6699
|
+
} catch (error) {
|
|
6700
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
6701
|
+
}
|
|
6702
|
+
}
|
|
6703
|
+
};
|
|
6704
|
+
var esvpSessionInspectTool = {
|
|
6705
|
+
name: "dlab.esvp.session.inspect",
|
|
6706
|
+
description: "Inspect an ESVP session and optionally include transcript and artifacts.",
|
|
6707
|
+
inputSchema: z10.object({
|
|
6708
|
+
serverUrl: z10.string().url().optional().describe("ESVP control-plane base URL"),
|
|
6709
|
+
sessionId: z10.string().describe("ESVP session ID"),
|
|
6710
|
+
includeTranscript: z10.boolean().optional(),
|
|
6711
|
+
includeArtifacts: z10.boolean().optional()
|
|
6712
|
+
}),
|
|
6713
|
+
handler: async (params) => {
|
|
6714
|
+
try {
|
|
6715
|
+
const inspection = await inspectESVPSession(
|
|
6716
|
+
params.sessionId,
|
|
6717
|
+
{
|
|
6718
|
+
includeTranscript: params.includeTranscript === true,
|
|
6719
|
+
includeArtifacts: params.includeArtifacts === true
|
|
6720
|
+
},
|
|
6721
|
+
params.serverUrl
|
|
6722
|
+
);
|
|
6723
|
+
return createJsonResult({
|
|
6724
|
+
...await makeBaseResult(params.serverUrl),
|
|
6725
|
+
...inspection
|
|
6726
|
+
});
|
|
6727
|
+
} catch (error) {
|
|
6728
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
6729
|
+
}
|
|
6730
|
+
}
|
|
6731
|
+
};
|
|
6732
|
+
var esvpSessionTranscriptTool = {
|
|
6733
|
+
name: "dlab.esvp.session.transcript",
|
|
6734
|
+
description: "Fetch the canonical public transcript for an ESVP session.",
|
|
6735
|
+
inputSchema: z10.object({
|
|
6736
|
+
serverUrl: z10.string().url().optional().describe("ESVP control-plane base URL"),
|
|
6737
|
+
sessionId: z10.string().describe("ESVP session ID")
|
|
6738
|
+
}),
|
|
6739
|
+
handler: async (params) => {
|
|
6740
|
+
try {
|
|
6741
|
+
const transcript = await getESVPTranscript(params.sessionId, params.serverUrl);
|
|
6742
|
+
return createJsonResult({
|
|
6743
|
+
...await makeBaseResult(params.serverUrl),
|
|
6744
|
+
sessionId: params.sessionId,
|
|
6745
|
+
transcript: transcript?.events || []
|
|
6746
|
+
});
|
|
6747
|
+
} catch (error) {
|
|
6748
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
6749
|
+
}
|
|
6750
|
+
}
|
|
6751
|
+
};
|
|
6752
|
+
var esvpSessionArtifactsListTool = {
|
|
6753
|
+
name: "dlab.esvp.session.artifacts.list",
|
|
6754
|
+
description: "List public artifacts for an ESVP session.",
|
|
6755
|
+
inputSchema: z10.object({
|
|
6756
|
+
serverUrl: z10.string().url().optional().describe("ESVP control-plane base URL"),
|
|
6757
|
+
sessionId: z10.string().describe("ESVP session ID")
|
|
6758
|
+
}),
|
|
6759
|
+
handler: async (params) => {
|
|
6760
|
+
try {
|
|
6761
|
+
const result = await listESVPArtifacts(params.sessionId, params.serverUrl);
|
|
6762
|
+
return createJsonResult({
|
|
6763
|
+
...await makeBaseResult(params.serverUrl),
|
|
6764
|
+
sessionId: params.sessionId,
|
|
6765
|
+
artifacts: result?.artifacts || []
|
|
6766
|
+
});
|
|
6767
|
+
} catch (error) {
|
|
6768
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
6769
|
+
}
|
|
6770
|
+
}
|
|
6771
|
+
};
|
|
6772
|
+
var esvpSessionArtifactGetTool = {
|
|
6773
|
+
name: "dlab.esvp.session.artifact.get",
|
|
6774
|
+
description: "Fetch the contents of a public ESVP artifact.",
|
|
6775
|
+
inputSchema: z10.object({
|
|
6776
|
+
serverUrl: z10.string().url().optional().describe("ESVP control-plane base URL"),
|
|
6777
|
+
sessionId: z10.string().describe("ESVP session ID"),
|
|
6778
|
+
artifactPath: z10.string().describe("Artifact relative path returned by artifacts.list")
|
|
6779
|
+
}),
|
|
6780
|
+
handler: async (params) => {
|
|
6781
|
+
try {
|
|
6782
|
+
const content = await getESVPArtifactContent(params.sessionId, params.artifactPath, params.serverUrl);
|
|
6783
|
+
return createJsonResult({
|
|
6784
|
+
...await makeBaseResult(params.serverUrl),
|
|
6785
|
+
sessionId: params.sessionId,
|
|
6786
|
+
artifactPath: params.artifactPath,
|
|
6787
|
+
content
|
|
6788
|
+
});
|
|
6789
|
+
} catch (error) {
|
|
6790
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
6791
|
+
}
|
|
6792
|
+
}
|
|
6793
|
+
};
|
|
6794
|
+
var esvpSessionActionsTool = {
|
|
6795
|
+
name: "dlab.esvp.session.actions",
|
|
6796
|
+
description: "Run public ESVP actions in an existing session. Use withNetwork to auto-configure the default App Lab external-proxy profile if not already active.",
|
|
6797
|
+
inputSchema: z10.object({
|
|
6798
|
+
serverUrl: z10.string().url().optional().describe("ESVP control-plane base URL"),
|
|
6799
|
+
sessionId: z10.string().describe("ESVP session ID"),
|
|
6800
|
+
actions: z10.array(actionSchema).min(1).describe("Public ESVP actions"),
|
|
6801
|
+
finish: z10.boolean().optional().describe("Finish the session after running the actions"),
|
|
6802
|
+
captureLogcat: z10.boolean().optional().describe("Capture logcat on finish when supported"),
|
|
6803
|
+
checkpointAfterEach: z10.boolean().optional().describe("Enable checkpointAfter for every action"),
|
|
6804
|
+
withNetwork: z10.boolean().optional().describe("Auto-configure the default App Lab external-proxy profile before running actions if not already configured")
|
|
6805
|
+
}),
|
|
6806
|
+
handler: async (params) => {
|
|
6807
|
+
try {
|
|
6808
|
+
let networkConfigured = null;
|
|
6809
|
+
if (params.withNetwork) {
|
|
6810
|
+
const networkState = await getESVPSessionNetwork(params.sessionId, params.serverUrl).catch(() => null);
|
|
6811
|
+
const hasActiveProfile = networkState?.network?.active_profile || networkState?.network?.effective_profile;
|
|
6812
|
+
if (!hasActiveProfile) {
|
|
6813
|
+
networkConfigured = await configureESVPNetwork(
|
|
6814
|
+
params.sessionId,
|
|
6815
|
+
buildAppLabNetworkProfile(
|
|
6816
|
+
{
|
|
6817
|
+
enabled: true,
|
|
6818
|
+
mode: "external-proxy",
|
|
6819
|
+
profile: "applab-standard-capture",
|
|
6820
|
+
label: "App Lab Standard Capture"
|
|
6821
|
+
},
|
|
6822
|
+
{
|
|
6823
|
+
platform: executorToPlatform(typeof networkState?.session?.executor === "string" ? networkState.session.executor : void 0),
|
|
6824
|
+
deviceId: typeof networkState?.session?.device_id === "string" ? networkState.session.device_id : void 0
|
|
6825
|
+
}
|
|
6826
|
+
) || {},
|
|
6827
|
+
params.serverUrl
|
|
6828
|
+
).catch((err) => ({ error: err instanceof Error ? err.message : String(err) }));
|
|
6829
|
+
}
|
|
6830
|
+
}
|
|
6831
|
+
const result = await runESVPActions(
|
|
6832
|
+
params.sessionId,
|
|
6833
|
+
{
|
|
6834
|
+
actions: normalizeActions(params.actions),
|
|
6835
|
+
finish: params.finish === true,
|
|
6836
|
+
captureLogcat: params.captureLogcat,
|
|
6837
|
+
checkpointAfterEach: params.checkpointAfterEach
|
|
6838
|
+
},
|
|
6839
|
+
params.serverUrl
|
|
6840
|
+
);
|
|
6841
|
+
return createJsonResult({
|
|
6842
|
+
...await makeBaseResult(params.serverUrl),
|
|
6843
|
+
...networkConfigured ? { networkConfigured } : {},
|
|
6844
|
+
...result
|
|
6845
|
+
});
|
|
6846
|
+
} catch (error) {
|
|
6847
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
6848
|
+
}
|
|
6849
|
+
}
|
|
6850
|
+
};
|
|
6851
|
+
var esvpSessionCheckpointTool = {
|
|
6852
|
+
name: "dlab.esvp.session.checkpoint",
|
|
6853
|
+
description: "Capture a public ESVP checkpoint for an existing session.",
|
|
6854
|
+
inputSchema: z10.object({
|
|
6855
|
+
serverUrl: z10.string().url().optional().describe("ESVP control-plane base URL"),
|
|
6856
|
+
sessionId: z10.string().describe("ESVP session ID"),
|
|
6857
|
+
label: z10.string().optional().describe("Optional checkpoint label")
|
|
6858
|
+
}),
|
|
6859
|
+
handler: async (params) => {
|
|
6860
|
+
try {
|
|
6861
|
+
const result = await captureESVPCheckpoint(
|
|
6862
|
+
params.sessionId,
|
|
6863
|
+
{
|
|
6864
|
+
...params.label ? { label: params.label } : {}
|
|
6865
|
+
},
|
|
6866
|
+
params.serverUrl
|
|
6867
|
+
);
|
|
6868
|
+
return createJsonResult({
|
|
6869
|
+
...await makeBaseResult(params.serverUrl),
|
|
6870
|
+
...result
|
|
6871
|
+
});
|
|
6872
|
+
} catch (error) {
|
|
6873
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
6874
|
+
}
|
|
6875
|
+
}
|
|
6876
|
+
};
|
|
6877
|
+
var esvpSessionFinishTool = {
|
|
6878
|
+
name: "dlab.esvp.session.finish",
|
|
6879
|
+
description: "Finish an ESVP session.",
|
|
6880
|
+
inputSchema: z10.object({
|
|
6881
|
+
serverUrl: z10.string().url().optional().describe("ESVP control-plane base URL"),
|
|
6882
|
+
sessionId: z10.string().describe("ESVP session ID"),
|
|
6883
|
+
captureLogcat: z10.boolean().optional().describe("Capture logcat on finish when supported")
|
|
6884
|
+
}),
|
|
6885
|
+
handler: async (params) => {
|
|
6886
|
+
try {
|
|
6887
|
+
const result = await finishESVPSession(
|
|
6888
|
+
params.sessionId,
|
|
6889
|
+
{
|
|
6890
|
+
captureLogcat: params.captureLogcat
|
|
6891
|
+
},
|
|
6892
|
+
params.serverUrl
|
|
6893
|
+
);
|
|
6894
|
+
return createJsonResult({
|
|
6895
|
+
...await makeBaseResult(params.serverUrl),
|
|
6896
|
+
...result
|
|
6897
|
+
});
|
|
6898
|
+
} catch (error) {
|
|
6899
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
6900
|
+
}
|
|
6901
|
+
}
|
|
6902
|
+
};
|
|
6903
|
+
var esvpReplayRunTool = {
|
|
6904
|
+
name: "dlab.esvp.replay.run",
|
|
6905
|
+
description: "Replay an ESVP session to a new session using the public replay endpoint.",
|
|
6906
|
+
inputSchema: z10.object({
|
|
6907
|
+
serverUrl: z10.string().url().optional().describe("ESVP control-plane base URL"),
|
|
6908
|
+
sessionId: z10.string().describe("Base ESVP session ID"),
|
|
6909
|
+
executor: z10.enum(["fake", "adb", "ios-sim", "maestro-ios"]).optional().describe("Replay executor"),
|
|
6910
|
+
deviceId: z10.string().optional().describe("Replay device ID"),
|
|
6911
|
+
captureLogcat: z10.boolean().optional(),
|
|
6912
|
+
meta: jsonObjectSchema.optional()
|
|
6913
|
+
}),
|
|
6914
|
+
handler: async (params) => {
|
|
6915
|
+
try {
|
|
6916
|
+
const result = await replayESVPSession(
|
|
6917
|
+
params.sessionId,
|
|
6918
|
+
{
|
|
6919
|
+
executor: params.executor,
|
|
6920
|
+
deviceId: params.deviceId,
|
|
6921
|
+
captureLogcat: params.captureLogcat,
|
|
6922
|
+
meta: {
|
|
6923
|
+
source: "applab-discovery",
|
|
6924
|
+
...params.meta || {}
|
|
6925
|
+
}
|
|
6926
|
+
},
|
|
6927
|
+
params.serverUrl
|
|
6928
|
+
);
|
|
6929
|
+
const consistency = result?.replay_session?.id ? await getESVPReplayConsistency(result.replay_session.id, params.serverUrl).catch(() => null) : null;
|
|
6930
|
+
return createJsonResult({
|
|
6931
|
+
...await makeBaseResult(params.serverUrl),
|
|
6932
|
+
...result,
|
|
6933
|
+
replayConsistency: consistency?.replay_consistency || null
|
|
6934
|
+
});
|
|
6935
|
+
} catch (error) {
|
|
6936
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
6937
|
+
}
|
|
6938
|
+
}
|
|
6939
|
+
};
|
|
6940
|
+
var esvpNetworkConfigureTool = {
|
|
6941
|
+
name: "dlab.esvp.network.configure",
|
|
6942
|
+
description: "Configure an ESVP network profile through the public network contract.",
|
|
6943
|
+
inputSchema: z10.object({
|
|
6944
|
+
serverUrl: z10.string().url().optional().describe("ESVP control-plane base URL"),
|
|
6945
|
+
sessionId: z10.string().describe("ESVP session ID"),
|
|
6946
|
+
profile: z10.string().optional(),
|
|
6947
|
+
label: z10.string().optional(),
|
|
6948
|
+
connectivity: z10.enum(["online", "offline", "reset"]).optional(),
|
|
6949
|
+
proxy: proxySchema.optional(),
|
|
6950
|
+
faults: jsonObjectSchema.optional(),
|
|
6951
|
+
capture: jsonObjectSchema.optional(),
|
|
6952
|
+
clear: z10.boolean().optional().describe("Clear the active network profile instead of applying one")
|
|
6953
|
+
}),
|
|
6954
|
+
handler: async (params) => {
|
|
6955
|
+
try {
|
|
6956
|
+
if (params.clear === true) {
|
|
6957
|
+
const cleared = await clearESVPNetwork(params.sessionId, params.serverUrl);
|
|
6958
|
+
return createJsonResult({
|
|
6959
|
+
...await makeBaseResult(params.serverUrl),
|
|
6960
|
+
...cleared
|
|
6961
|
+
});
|
|
6962
|
+
}
|
|
6963
|
+
const result = await configureESVPNetwork(
|
|
6964
|
+
params.sessionId,
|
|
6965
|
+
{
|
|
6966
|
+
...params.profile ? { profile: params.profile } : {},
|
|
6967
|
+
...params.label ? { label: params.label } : {},
|
|
6968
|
+
...params.connectivity ? { connectivity: params.connectivity } : {},
|
|
6969
|
+
...params.proxy ? { proxy: params.proxy } : {},
|
|
6970
|
+
...params.faults ? { faults: params.faults } : {},
|
|
6971
|
+
...params.capture ? { capture: params.capture } : {}
|
|
6972
|
+
},
|
|
6973
|
+
params.serverUrl
|
|
6974
|
+
);
|
|
6975
|
+
return createJsonResult({
|
|
6976
|
+
...await makeBaseResult(params.serverUrl),
|
|
6977
|
+
...result
|
|
6978
|
+
});
|
|
6979
|
+
} catch (error) {
|
|
6980
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
6981
|
+
}
|
|
6982
|
+
}
|
|
6983
|
+
};
|
|
6984
|
+
var esvpSessionNetworkTool = {
|
|
6985
|
+
name: "dlab.esvp.session.network",
|
|
6986
|
+
description: "Read the public network state for an ESVP session.",
|
|
6987
|
+
inputSchema: z10.object({
|
|
6988
|
+
serverUrl: z10.string().url().optional().describe("ESVP control-plane base URL"),
|
|
6989
|
+
sessionId: z10.string().describe("ESVP session ID")
|
|
6990
|
+
}),
|
|
6991
|
+
handler: async (params) => {
|
|
6992
|
+
try {
|
|
6993
|
+
const result = await getESVPSessionNetwork(params.sessionId, params.serverUrl);
|
|
6994
|
+
return createJsonResult({
|
|
6995
|
+
...await makeBaseResult(params.serverUrl),
|
|
6996
|
+
...result
|
|
6997
|
+
});
|
|
6998
|
+
} catch (error) {
|
|
6999
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
7000
|
+
}
|
|
7001
|
+
}
|
|
7002
|
+
};
|
|
7003
|
+
var esvpNetworkTraceAttachTool = {
|
|
7004
|
+
name: "dlab.esvp.network.trace.attach",
|
|
7005
|
+
description: "Attach a network trace artifact to an ESVP session using the public contract.",
|
|
7006
|
+
inputSchema: z10.object({
|
|
7007
|
+
serverUrl: z10.string().url().optional().describe("ESVP control-plane base URL"),
|
|
7008
|
+
sessionId: z10.string().describe("ESVP session ID"),
|
|
7009
|
+
traceKind: z10.string().describe("Trace kind, e.g. http_trace or har"),
|
|
7010
|
+
label: z10.string().optional(),
|
|
7011
|
+
source: z10.string().optional(),
|
|
7012
|
+
requestId: z10.string().optional(),
|
|
7013
|
+
method: z10.string().optional(),
|
|
7014
|
+
url: z10.string().optional(),
|
|
7015
|
+
statusCode: z10.number().optional(),
|
|
7016
|
+
format: z10.string().optional(),
|
|
7017
|
+
payload: z10.any().optional().describe("Trace payload object/string"),
|
|
7018
|
+
traceFilePath: z10.string().optional().describe("Optional local file path to JSON/text trace payload")
|
|
7019
|
+
}),
|
|
7020
|
+
handler: async (params) => {
|
|
7021
|
+
try {
|
|
7022
|
+
const payload = await resolveTracePayload({
|
|
7023
|
+
payload: params.payload,
|
|
7024
|
+
traceFilePath: params.traceFilePath
|
|
7025
|
+
});
|
|
7026
|
+
const result = await attachESVPNetworkTrace(
|
|
7027
|
+
params.sessionId,
|
|
7028
|
+
{
|
|
7029
|
+
trace_kind: params.traceKind,
|
|
7030
|
+
...params.label ? { label: params.label } : {},
|
|
7031
|
+
...params.source ? { source: params.source } : {},
|
|
7032
|
+
...params.requestId ? { request_id: params.requestId } : {},
|
|
7033
|
+
...params.method ? { method: params.method } : {},
|
|
7034
|
+
...params.url ? { url: params.url } : {},
|
|
7035
|
+
...params.statusCode !== void 0 ? { status_code: params.statusCode } : {},
|
|
7036
|
+
...params.format ? { format: params.format } : {},
|
|
7037
|
+
payload
|
|
7038
|
+
},
|
|
7039
|
+
params.serverUrl
|
|
7040
|
+
);
|
|
7041
|
+
return createJsonResult({
|
|
7042
|
+
...await makeBaseResult(params.serverUrl),
|
|
7043
|
+
...result
|
|
7044
|
+
});
|
|
7045
|
+
} catch (error) {
|
|
7046
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
7047
|
+
}
|
|
7048
|
+
}
|
|
7049
|
+
};
|
|
7050
|
+
var esvpSessionGetTool = {
|
|
7051
|
+
name: "dlab.esvp.session.get",
|
|
7052
|
+
description: "Get the latest public summary for an ESVP session.",
|
|
7053
|
+
inputSchema: z10.object({
|
|
7054
|
+
serverUrl: z10.string().url().optional().describe("ESVP control-plane base URL"),
|
|
7055
|
+
sessionId: z10.string().describe("ESVP session ID")
|
|
7056
|
+
}),
|
|
7057
|
+
handler: async (params) => {
|
|
7058
|
+
try {
|
|
7059
|
+
const result = await getESVPSession(params.sessionId, params.serverUrl);
|
|
7060
|
+
return createJsonResult({
|
|
7061
|
+
...await makeBaseResult(params.serverUrl),
|
|
7062
|
+
...result
|
|
7063
|
+
});
|
|
7064
|
+
} catch (error) {
|
|
7065
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
7066
|
+
}
|
|
7067
|
+
}
|
|
7068
|
+
};
|
|
7069
|
+
var esvpReplayValidateTool = {
|
|
7070
|
+
name: "dlab.esvp.replay.validate",
|
|
7071
|
+
description: "Validate whether an ESVP session supports canonical replay and inspect the result.",
|
|
7072
|
+
inputSchema: z10.object({
|
|
7073
|
+
serverUrl: z10.string().url().optional().describe("ESVP control-plane base URL"),
|
|
7074
|
+
sessionId: z10.string().describe("ESVP session ID")
|
|
7075
|
+
}),
|
|
7076
|
+
handler: async (params) => {
|
|
7077
|
+
try {
|
|
7078
|
+
const result = await validateESVPReplay(params.sessionId, params.serverUrl);
|
|
7079
|
+
return createJsonResult({
|
|
7080
|
+
...await makeBaseResult(params.serverUrl),
|
|
7081
|
+
...result
|
|
7082
|
+
});
|
|
7083
|
+
} catch (error) {
|
|
7084
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
7085
|
+
}
|
|
7086
|
+
}
|
|
7087
|
+
};
|
|
7088
|
+
var preflightRuleSchema = z10.object({
|
|
7089
|
+
kind: z10.enum(["permission", "dismiss_dialog", "wait_for_stable", "clear_data", "set_setting"]).describe("Preflight rule kind"),
|
|
7090
|
+
permission: z10.string().optional().describe("Android permission (for kind=permission)"),
|
|
7091
|
+
action: z10.enum(["grant", "revoke"]).optional().describe("Permission action (for kind=permission)"),
|
|
7092
|
+
selector: z10.string().optional().describe("Selector to find and dismiss (for kind=dismiss_dialog)"),
|
|
7093
|
+
timeout_ms: z10.number().optional().describe("Timeout in ms (for kind=wait_for_stable/dismiss_dialog)"),
|
|
7094
|
+
poll_ms: z10.number().optional().describe("Poll interval in ms (for kind=wait_for_stable)"),
|
|
7095
|
+
namespace: z10.string().optional().describe("Settings namespace: system|secure|global (for kind=set_setting)"),
|
|
7096
|
+
key: z10.string().optional().describe("Setting key (for kind=set_setting)"),
|
|
7097
|
+
value: z10.string().optional().describe("Setting value (for kind=set_setting)")
|
|
7098
|
+
});
|
|
7099
|
+
var esvpSessionPreflightTool = {
|
|
7100
|
+
name: "dlab.esvp.session.preflight",
|
|
7101
|
+
description: "Run preflight/bootstrap rules on an ESVP session before executing actions. Supports permission grants, dialog dismissal, wait for stable UI, clear data, and system settings.",
|
|
7102
|
+
inputSchema: z10.object({
|
|
7103
|
+
serverUrl: z10.string().url().optional().describe("ESVP control-plane base URL"),
|
|
7104
|
+
sessionId: z10.string().describe("ESVP session ID"),
|
|
7105
|
+
policy: z10.string().optional().describe("Preflight policy name (e.g. fresh_install)"),
|
|
7106
|
+
appId: z10.string().optional().describe("Target app ID (e.g. com.example.app)"),
|
|
7107
|
+
rules: z10.array(preflightRuleSchema).optional().describe("Preflight rules to execute")
|
|
7108
|
+
}),
|
|
7109
|
+
handler: async (params) => {
|
|
7110
|
+
try {
|
|
7111
|
+
const config = {
|
|
7112
|
+
...params.policy ? { policy: params.policy } : {},
|
|
7113
|
+
...params.appId ? { appId: params.appId } : {},
|
|
7114
|
+
...params.rules ? { rules: params.rules } : {}
|
|
7115
|
+
};
|
|
7116
|
+
const result = await runESVPPreflight(params.sessionId, config, params.serverUrl);
|
|
7117
|
+
return createJsonResult({
|
|
7118
|
+
...await makeBaseResult(params.serverUrl),
|
|
7119
|
+
...result
|
|
7120
|
+
});
|
|
7121
|
+
} catch (error) {
|
|
7122
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
7123
|
+
}
|
|
7124
|
+
}
|
|
7125
|
+
};
|
|
7126
|
+
var projectESVPCurrentTool = {
|
|
7127
|
+
name: "dlab.project.esvp.current",
|
|
7128
|
+
description: "Read the current ESVP state stored for an App Lab mobile recording/project without requiring the web UI.",
|
|
7129
|
+
inputSchema: projectRecordingSchema,
|
|
7130
|
+
handler: async (params) => {
|
|
7131
|
+
try {
|
|
7132
|
+
const session = await readMobileRecordingSession(params.recordingId);
|
|
7133
|
+
const esvp = resolveProjectRecordingESVPState(session);
|
|
7134
|
+
const validation = esvp?.validation && typeof esvp.validation === "object" ? esvp.validation : null;
|
|
7135
|
+
const network = esvp?.network && typeof esvp.network === "object" ? esvp.network : null;
|
|
7136
|
+
return createJsonResult({
|
|
7137
|
+
recordingId: params.recordingId,
|
|
7138
|
+
recordingDir: getMobileRecordingDir(params.recordingId),
|
|
7139
|
+
sessionPath: getMobileRecordingSessionPath(params.recordingId),
|
|
7140
|
+
recording: {
|
|
7141
|
+
id: String(session.id || params.recordingId),
|
|
7142
|
+
name: String(session.name || `Recording ${params.recordingId}`),
|
|
7143
|
+
platform: session.platform === "ios" ? "ios" : "android",
|
|
7144
|
+
deviceId: typeof session.deviceId === "string" ? session.deviceId : null,
|
|
7145
|
+
deviceName: typeof session.deviceName === "string" ? session.deviceName : null,
|
|
7146
|
+
appId: typeof session.appId === "string" ? session.appId : null,
|
|
7147
|
+
actionsCount: Array.isArray(session.actions) ? session.actions.length : 0
|
|
7148
|
+
},
|
|
7149
|
+
esvp: {
|
|
7150
|
+
serverUrl: resolveProjectRecordingESVPServerUrl(session) || null,
|
|
7151
|
+
connectionMode: typeof esvp?.connectionMode === "string" ? esvp.connectionMode : null,
|
|
7152
|
+
executor: typeof esvp?.executor === "string" ? esvp.executor : null,
|
|
7153
|
+
currentSessionId: typeof esvp?.currentSessionId === "string" ? esvp.currentSessionId : null,
|
|
7154
|
+
sourceSessionId: resolveProjectRecordingESVPSourceSessionId(session),
|
|
7155
|
+
replaySessionId: resolveProjectRecordingReplaySessionId(session),
|
|
7156
|
+
validationSupported: validation?.supported === false ? false : validation ? true : null,
|
|
7157
|
+
validatedAt: typeof validation?.validatedAt === "string" ? validation.validatedAt : null,
|
|
7158
|
+
replayedAt: typeof validation?.replayedAt === "string" ? validation.replayedAt : null,
|
|
7159
|
+
replayConsistency: validation?.replayConsistency || null,
|
|
7160
|
+
checkpointComparison: validation?.checkpointComparison || null,
|
|
7161
|
+
network: {
|
|
7162
|
+
entryCount: Number.isFinite(Number(network?.entryCount)) ? Number(network?.entryCount) : 0,
|
|
7163
|
+
traceCount: Number.isFinite(Number(network?.traceCount)) ? Number(network?.traceCount) : 0,
|
|
7164
|
+
traceKinds: Array.isArray(network?.traceKinds) ? network.traceKinds : [],
|
|
7165
|
+
captureStatus: typeof network?.captureStatus === "string" ? network.captureStatus : null,
|
|
7166
|
+
networkSupported: typeof network?.networkSupported === "boolean" ? network.networkSupported : null,
|
|
7167
|
+
activeProfile: network?.activeProfile || null,
|
|
7168
|
+
effectiveProfile: network?.effectiveProfile || null,
|
|
7169
|
+
captureProxy: network?.captureProxy || null,
|
|
7170
|
+
appTraceCollector: network?.appTraceCollector || null,
|
|
7171
|
+
managedProxy: network?.managedProxy || null,
|
|
7172
|
+
syncedAt: typeof network?.syncedAt === "string" ? network.syncedAt : null
|
|
7173
|
+
}
|
|
7174
|
+
}
|
|
7175
|
+
});
|
|
7176
|
+
} catch (error) {
|
|
7177
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
7178
|
+
}
|
|
7179
|
+
}
|
|
7180
|
+
};
|
|
7181
|
+
var projectESVPValidateTool = {
|
|
7182
|
+
name: "dlab.project.esvp.validate",
|
|
7183
|
+
description: "Run the App Lab ESVP validation flow for a mobile recording/project. Requires the local App Lab server to be running.",
|
|
7184
|
+
inputSchema: projectRecordingSchema.extend({
|
|
7185
|
+
appLabUrl: z10.string().url().optional().describe("App Lab server base URL, defaults to http://127.0.0.1:3847"),
|
|
7186
|
+
serverUrl: z10.string().url().optional().describe("Optional ESVP control-plane base URL"),
|
|
7187
|
+
replay: z10.boolean().optional().describe("Run replay after source validation"),
|
|
7188
|
+
captureLogcat: z10.boolean().optional(),
|
|
7189
|
+
profileId: projectValidationProfileIdSchema.optional().describe("Convenience network profile shortcut"),
|
|
7190
|
+
network: projectValidationNetworkSchema.optional().describe("Explicit network profile payload. Overrides profileId. Pass null to validate without network capture.")
|
|
7191
|
+
}),
|
|
7192
|
+
handler: async (params) => {
|
|
7193
|
+
try {
|
|
7194
|
+
const network = params.network === null ? null : buildProjectValidationNetwork(params.profileId, params.network);
|
|
7195
|
+
const result = await callAppLabJson(
|
|
7196
|
+
`/api/testing/mobile/recordings/${encodeURIComponent(params.recordingId)}/esvp/validate`,
|
|
7197
|
+
{
|
|
7198
|
+
method: "POST",
|
|
7199
|
+
headers: { "content-type": "application/json" },
|
|
7200
|
+
body: JSON.stringify({
|
|
7201
|
+
...params.serverUrl ? { serverUrl: params.serverUrl } : {},
|
|
7202
|
+
...params.captureLogcat !== void 0 ? { captureLogcat: params.captureLogcat } : {},
|
|
7203
|
+
...params.replay !== void 0 ? { replay: params.replay } : {},
|
|
7204
|
+
...network ? { network } : {}
|
|
7205
|
+
})
|
|
7206
|
+
},
|
|
7207
|
+
params.appLabUrl
|
|
7208
|
+
);
|
|
7209
|
+
return createJsonResult({
|
|
7210
|
+
appLabUrl: result.appLabUrl,
|
|
7211
|
+
recordingId: params.recordingId,
|
|
7212
|
+
result: result.payload
|
|
7213
|
+
});
|
|
7214
|
+
} catch (error) {
|
|
7215
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
7216
|
+
}
|
|
7217
|
+
}
|
|
7218
|
+
};
|
|
7219
|
+
var projectESVPReplayTool = {
|
|
7220
|
+
name: "dlab.project.esvp.replay",
|
|
7221
|
+
description: "Replay the canonical ESVP source session already attached to a mobile recording/project. Requires the local App Lab server to be running.",
|
|
7222
|
+
inputSchema: projectRecordingSchema.extend({
|
|
7223
|
+
appLabUrl: z10.string().url().optional().describe("App Lab server base URL, defaults to http://127.0.0.1:3847"),
|
|
7224
|
+
serverUrl: z10.string().url().optional().describe("Optional ESVP control-plane base URL"),
|
|
7225
|
+
captureLogcat: z10.boolean().optional()
|
|
7226
|
+
}),
|
|
7227
|
+
handler: async (params) => {
|
|
7228
|
+
try {
|
|
7229
|
+
const result = await callAppLabJson(
|
|
7230
|
+
`/api/testing/mobile/recordings/${encodeURIComponent(params.recordingId)}/esvp/replay`,
|
|
7231
|
+
{
|
|
7232
|
+
method: "POST",
|
|
7233
|
+
headers: { "content-type": "application/json" },
|
|
7234
|
+
body: JSON.stringify({
|
|
7235
|
+
...params.serverUrl ? { serverUrl: params.serverUrl } : {},
|
|
7236
|
+
...params.captureLogcat !== void 0 ? { captureLogcat: params.captureLogcat } : {}
|
|
7237
|
+
})
|
|
7238
|
+
},
|
|
7239
|
+
params.appLabUrl
|
|
7240
|
+
);
|
|
7241
|
+
return createJsonResult({
|
|
7242
|
+
appLabUrl: result.appLabUrl,
|
|
7243
|
+
recordingId: params.recordingId,
|
|
7244
|
+
result: result.payload
|
|
7245
|
+
});
|
|
7246
|
+
} catch (error) {
|
|
7247
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
7248
|
+
}
|
|
7249
|
+
}
|
|
7250
|
+
};
|
|
7251
|
+
var projectESVPSyncNetworkTool = {
|
|
7252
|
+
name: "dlab.project.esvp.sync_network",
|
|
7253
|
+
description: "Sync the latest attached ESVP network trace into an App Lab mobile recording/project. Requires the local App Lab server to be running.",
|
|
7254
|
+
inputSchema: projectRecordingSchema.extend({
|
|
7255
|
+
appLabUrl: z10.string().url().optional().describe("App Lab server base URL, defaults to http://127.0.0.1:3847"),
|
|
7256
|
+
serverUrl: z10.string().url().optional().describe("Optional ESVP control-plane base URL"),
|
|
7257
|
+
sessionId: z10.string().optional().describe("Optional ESVP session ID override")
|
|
7258
|
+
}),
|
|
7259
|
+
handler: async (params) => {
|
|
7260
|
+
try {
|
|
7261
|
+
const result = await callAppLabJson(
|
|
7262
|
+
`/api/testing/mobile/recordings/${encodeURIComponent(params.recordingId)}/esvp/sync-network`,
|
|
7263
|
+
{
|
|
7264
|
+
method: "POST",
|
|
7265
|
+
headers: { "content-type": "application/json" },
|
|
7266
|
+
body: JSON.stringify({
|
|
7267
|
+
...params.serverUrl ? { serverUrl: params.serverUrl } : {},
|
|
7268
|
+
...params.sessionId ? { sessionId: params.sessionId } : {}
|
|
7269
|
+
})
|
|
7270
|
+
},
|
|
7271
|
+
params.appLabUrl
|
|
7272
|
+
);
|
|
7273
|
+
return createJsonResult({
|
|
7274
|
+
appLabUrl: result.appLabUrl,
|
|
7275
|
+
recordingId: params.recordingId,
|
|
7276
|
+
result: result.payload
|
|
7277
|
+
});
|
|
7278
|
+
} catch (error) {
|
|
7279
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
7280
|
+
}
|
|
7281
|
+
}
|
|
7282
|
+
};
|
|
7283
|
+
var projectESVPAppTraceBootstrapTool = {
|
|
7284
|
+
name: "dlab.project.esvp.app_trace_bootstrap",
|
|
7285
|
+
description: "Fetch the active local app_http_trace bootstrap config for an App Lab mobile recording/project. Requires the local App Lab server to be running when a collector is active.",
|
|
7286
|
+
inputSchema: projectRecordingSchema.extend({
|
|
7287
|
+
appLabUrl: z10.string().url().optional().describe("App Lab server base URL, defaults to http://127.0.0.1:3847"),
|
|
7288
|
+
appId: z10.string().optional().describe("Optional app ID override when multiple collectors are active")
|
|
7289
|
+
}),
|
|
7290
|
+
handler: async (params) => {
|
|
7291
|
+
try {
|
|
7292
|
+
const session = await readMobileRecordingSession(params.recordingId);
|
|
7293
|
+
const collector = session?.esvp?.network?.appTraceCollector || null;
|
|
7294
|
+
const searchParams = new URLSearchParams();
|
|
7295
|
+
if (params.recordingId) searchParams.set("recordingId", params.recordingId);
|
|
7296
|
+
if (params.appId) searchParams.set("appId", params.appId);
|
|
7297
|
+
const path10 = `/api/testing/mobile/app-http-trace/bootstrap?${searchParams.toString()}`;
|
|
7298
|
+
try {
|
|
7299
|
+
const result = await callAppLabJson(path10, { method: "GET" }, params.appLabUrl);
|
|
7300
|
+
return createJsonResult({
|
|
7301
|
+
appLabUrl: result.appLabUrl,
|
|
7302
|
+
recordingId: params.recordingId,
|
|
7303
|
+
result: result.payload
|
|
7304
|
+
});
|
|
7305
|
+
} catch (error) {
|
|
7306
|
+
return createJsonResult({
|
|
7307
|
+
appLabUrl: resolveAppLabBaseUrl(params.appLabUrl),
|
|
7308
|
+
recordingId: params.recordingId,
|
|
7309
|
+
activeCollector: collector,
|
|
7310
|
+
bootstrap: null,
|
|
7311
|
+
warning: error instanceof Error ? error.message : String(error)
|
|
7312
|
+
});
|
|
7313
|
+
}
|
|
7314
|
+
} catch (error) {
|
|
7315
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
7316
|
+
}
|
|
7317
|
+
}
|
|
7318
|
+
};
|
|
7319
|
+
var esvpTools = [
|
|
7320
|
+
esvpStatusTool,
|
|
7321
|
+
esvpDevicesTool,
|
|
7322
|
+
esvpSessionsListTool,
|
|
7323
|
+
esvpSessionCreateTool,
|
|
7324
|
+
esvpSessionGetTool,
|
|
7325
|
+
esvpSessionInspectTool,
|
|
7326
|
+
esvpSessionTranscriptTool,
|
|
7327
|
+
esvpSessionArtifactsListTool,
|
|
7328
|
+
esvpSessionArtifactGetTool,
|
|
7329
|
+
esvpSessionActionsTool,
|
|
7330
|
+
esvpSessionCheckpointTool,
|
|
7331
|
+
esvpSessionFinishTool,
|
|
7332
|
+
esvpSessionPreflightTool,
|
|
7333
|
+
esvpReplayRunTool,
|
|
7334
|
+
esvpReplayValidateTool,
|
|
7335
|
+
esvpSessionNetworkTool,
|
|
7336
|
+
esvpNetworkConfigureTool,
|
|
7337
|
+
esvpNetworkTraceAttachTool,
|
|
7338
|
+
projectESVPCurrentTool,
|
|
7339
|
+
projectESVPValidateTool,
|
|
7340
|
+
projectESVPReplayTool,
|
|
7341
|
+
projectESVPSyncNetworkTool,
|
|
7342
|
+
projectESVPAppTraceBootstrapTool
|
|
7343
|
+
];
|
|
7344
|
+
|
|
7345
|
+
// src/mcp/tools/templates.ts
|
|
7346
|
+
import { z as z11 } from "zod";
|
|
7347
|
+
var templateListTool = {
|
|
7348
|
+
name: "dlab.template.list",
|
|
7349
|
+
description: "List available Remotion video templates. Returns installed templates with their metadata, or indicates templates are not installed.",
|
|
7350
|
+
inputSchema: z11.object({}),
|
|
7351
|
+
handler: async () => {
|
|
7352
|
+
const installed = isTemplatesInstalled();
|
|
7353
|
+
if (!installed) {
|
|
7354
|
+
return createJsonResult({
|
|
7355
|
+
installed: false,
|
|
7356
|
+
templates: [],
|
|
7357
|
+
message: "Templates not installed. Install discoverylab-templates to ~/.discoverylab/templates/"
|
|
7358
|
+
});
|
|
7359
|
+
}
|
|
7360
|
+
const templates = getAvailableTemplates();
|
|
7361
|
+
return createJsonResult({
|
|
7362
|
+
installed: true,
|
|
7363
|
+
templates: templates.map((t) => ({
|
|
7364
|
+
id: t.id,
|
|
7365
|
+
name: t.name,
|
|
7366
|
+
description: t.description,
|
|
7367
|
+
resolution: `${t.width}x${t.height}`,
|
|
7368
|
+
fps: t.fps,
|
|
7369
|
+
durationFrames: t.durationFrames
|
|
7370
|
+
}))
|
|
7371
|
+
});
|
|
7372
|
+
}
|
|
7373
|
+
};
|
|
7374
|
+
var templateRenderTool = {
|
|
7375
|
+
name: "dlab.template.render",
|
|
7376
|
+
description: "Render a project video with a Remotion template. Starts an async render job and returns the job ID. Use the web UI to monitor progress.",
|
|
7377
|
+
inputSchema: z11.object({
|
|
7378
|
+
projectId: z11.string().describe("The project ID to render"),
|
|
7379
|
+
templateId: z11.enum(["studio", "showcase"]).describe('Template to use: "studio" (device + terminal) or "showcase" (floating device + artistic title + terminal)')
|
|
7380
|
+
}),
|
|
7381
|
+
handler: async ({ projectId, templateId }) => {
|
|
7382
|
+
if (!isTemplatesInstalled()) {
|
|
7383
|
+
return createErrorResult("Templates not installed. Install discoverylab-templates first.");
|
|
7384
|
+
}
|
|
7385
|
+
return createTextResult(
|
|
7386
|
+
`To render project "${projectId}" with template "${templateId}", use the web UI or call POST /api/templates/render with { projectId: "${projectId}", templateId: "${templateId}" }. The render runs asynchronously and progress is broadcast via WebSocket.`
|
|
7387
|
+
);
|
|
7388
|
+
}
|
|
7389
|
+
};
|
|
7390
|
+
var templateTools = [
|
|
7391
|
+
templateListTool,
|
|
7392
|
+
templateRenderTool
|
|
7393
|
+
];
|
|
7394
|
+
|
|
6317
7395
|
export {
|
|
6318
7396
|
uiOpenTool,
|
|
6319
7397
|
uiStatusTool,
|
|
@@ -6393,5 +7471,31 @@ export {
|
|
|
6393
7471
|
taskHubRequirementsGetTool,
|
|
6394
7472
|
taskHubTestMapGetTool,
|
|
6395
7473
|
taskHubTestMapToggleTool,
|
|
6396
|
-
taskHubTools
|
|
7474
|
+
taskHubTools,
|
|
7475
|
+
esvpStatusTool,
|
|
7476
|
+
esvpDevicesTool,
|
|
7477
|
+
esvpSessionsListTool,
|
|
7478
|
+
esvpSessionCreateTool,
|
|
7479
|
+
esvpSessionInspectTool,
|
|
7480
|
+
esvpSessionTranscriptTool,
|
|
7481
|
+
esvpSessionArtifactsListTool,
|
|
7482
|
+
esvpSessionArtifactGetTool,
|
|
7483
|
+
esvpSessionActionsTool,
|
|
7484
|
+
esvpSessionCheckpointTool,
|
|
7485
|
+
esvpSessionFinishTool,
|
|
7486
|
+
esvpReplayRunTool,
|
|
7487
|
+
esvpNetworkConfigureTool,
|
|
7488
|
+
esvpSessionNetworkTool,
|
|
7489
|
+
esvpNetworkTraceAttachTool,
|
|
7490
|
+
esvpSessionGetTool,
|
|
7491
|
+
esvpReplayValidateTool,
|
|
7492
|
+
projectESVPCurrentTool,
|
|
7493
|
+
projectESVPValidateTool,
|
|
7494
|
+
projectESVPReplayTool,
|
|
7495
|
+
projectESVPSyncNetworkTool,
|
|
7496
|
+
projectESVPAppTraceBootstrapTool,
|
|
7497
|
+
esvpTools,
|
|
7498
|
+
templateListTool,
|
|
7499
|
+
templateRenderTool,
|
|
7500
|
+
templateTools
|
|
6397
7501
|
};
|