@swmansion/argent 0.16.2-next.9 → 0.17.1-next.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/dist/cli-cmds.mjs +1710 -1594
- package/dist/installer.mjs +67 -63
- package/dist/mcp-server.mjs +306 -188
- package/dist/tool-server.cjs +430 -131
- package/package.json +1 -1
- package/skills/argent-create-flow/SKILL.md +3 -1
- package/skills/argent-device-interact/SKILL.md +1 -1
- package/skills/argent-screen-recording/SKILL.md +6 -2
package/dist/tool-server.cjs
CHANGED
|
@@ -109,7 +109,9 @@ var init_artifacts = __esm({
|
|
|
109
109
|
".xml": "application/xml",
|
|
110
110
|
".txt": "text/plain",
|
|
111
111
|
".md": "text/markdown",
|
|
112
|
-
".html": "text/html"
|
|
112
|
+
".html": "text/html",
|
|
113
|
+
".mp4": "video/mp4",
|
|
114
|
+
".mov": "video/quicktime"
|
|
113
115
|
};
|
|
114
116
|
ArtifactStore = class {
|
|
115
117
|
entries = /* @__PURE__ */ new Map();
|
|
@@ -138,6 +140,7 @@ var init_artifacts = __esm({
|
|
|
138
140
|
};
|
|
139
141
|
if (mtimeMs != null) handle.mtimeMs = mtimeMs;
|
|
140
142
|
if (isDirectory2) handle.archive = "tar.gz";
|
|
143
|
+
if (opts?.saveDir) handle.saveDir = opts.saveDir;
|
|
141
144
|
return handle;
|
|
142
145
|
}
|
|
143
146
|
get(id) {
|
|
@@ -382,6 +385,7 @@ var init_failure_codes = __esm({
|
|
|
382
385
|
SCREEN_RECORDING_NO_ACTIVE_SESSION: "SCREEN_RECORDING_NO_ACTIVE_SESSION",
|
|
383
386
|
SCREEN_RECORDING_STOP_IN_PROGRESS: "SCREEN_RECORDING_STOP_IN_PROGRESS",
|
|
384
387
|
SCREEN_RECORDING_START_EXITED: "SCREEN_RECORDING_START_EXITED",
|
|
388
|
+
SCREEN_RECORDING_START_TIMEOUT: "SCREEN_RECORDING_START_TIMEOUT",
|
|
385
389
|
SCREEN_RECORDING_PROCESS_ERROR: "SCREEN_RECORDING_PROCESS_ERROR",
|
|
386
390
|
SCREEN_RECORDING_OUTPUT_MISSING: "SCREEN_RECORDING_OUTPUT_MISSING",
|
|
387
391
|
SCREEN_RECORDING_SERVER_SHUTTING_DOWN: "SCREEN_RECORDING_SERVER_SHUTTING_DOWN",
|
|
@@ -34931,7 +34935,7 @@ var require_view = __commonJS({
|
|
|
34931
34935
|
var path38 = require("node:path");
|
|
34932
34936
|
var fs50 = require("node:fs");
|
|
34933
34937
|
var dirname14 = path38.dirname;
|
|
34934
|
-
var
|
|
34938
|
+
var basename10 = path38.basename;
|
|
34935
34939
|
var extname4 = path38.extname;
|
|
34936
34940
|
var join46 = path38.join;
|
|
34937
34941
|
var resolve7 = path38.resolve;
|
|
@@ -34970,7 +34974,7 @@ var require_view = __commonJS({
|
|
|
34970
34974
|
var root2 = roots[i];
|
|
34971
34975
|
var loc = resolve7(root2, name);
|
|
34972
34976
|
var dir = dirname14(loc);
|
|
34973
|
-
var file2 =
|
|
34977
|
+
var file2 = basename10(loc);
|
|
34974
34978
|
path39 = this.resolve(dir, file2);
|
|
34975
34979
|
}
|
|
34976
34980
|
return path39;
|
|
@@ -35000,7 +35004,7 @@ var require_view = __commonJS({
|
|
|
35000
35004
|
if (stat4 && stat4.isFile()) {
|
|
35001
35005
|
return path39;
|
|
35002
35006
|
}
|
|
35003
|
-
path39 = join46(dir,
|
|
35007
|
+
path39 = join46(dir, basename10(file2, ext), "index" + ext);
|
|
35004
35008
|
stat4 = tryStat(path39);
|
|
35005
35009
|
if (stat4 && stat4.isFile()) {
|
|
35006
35010
|
return path39;
|
|
@@ -57445,9 +57449,9 @@ var require_content_disposition = __commonJS({
|
|
|
57445
57449
|
if (typeof fallback === "string" && NON_LATIN1_REGEXP.test(fallback)) {
|
|
57446
57450
|
throw new TypeError("fallback must be ISO-8859-1 string");
|
|
57447
57451
|
}
|
|
57448
|
-
var name =
|
|
57452
|
+
var name = basename10(filename);
|
|
57449
57453
|
var isQuotedString = TEXT_REGEXP.test(name);
|
|
57450
|
-
var fallbackName = typeof fallback !== "string" ? fallback && getlatin1(name) :
|
|
57454
|
+
var fallbackName = typeof fallback !== "string" ? fallback && getlatin1(name) : basename10(fallback);
|
|
57451
57455
|
var hasFallback = typeof fallbackName === "string" && fallbackName !== name;
|
|
57452
57456
|
if (hasFallback || !isQuotedString || hasHexEscape(name)) {
|
|
57453
57457
|
params["filename*"] = name;
|
|
@@ -57567,7 +57571,7 @@ var require_content_disposition = __commonJS({
|
|
|
57567
57571
|
this.type = type;
|
|
57568
57572
|
this.parameters = parameters;
|
|
57569
57573
|
}
|
|
57570
|
-
function
|
|
57574
|
+
function basename10(path38) {
|
|
57571
57575
|
const normalized = path38.replaceAll("\\", "/");
|
|
57572
57576
|
let end = normalized.length;
|
|
57573
57577
|
while (end > 0 && normalized[end - 1] === "/") {
|
|
@@ -104489,7 +104493,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
104489
104493
|
var SESSION_ID = (0, import_node_crypto3.randomUUID)();
|
|
104490
104494
|
function readCliVersion() {
|
|
104491
104495
|
if (true) {
|
|
104492
|
-
return "0.
|
|
104496
|
+
return "0.17.0";
|
|
104493
104497
|
}
|
|
104494
104498
|
return "0.0.0";
|
|
104495
104499
|
}
|
|
@@ -105877,7 +105881,7 @@ var import_node_path5 = __toESM(require("node:path"));
|
|
|
105877
105881
|
var import_semver = __toESM(require_semver2());
|
|
105878
105882
|
|
|
105879
105883
|
// ../tool-server/package.json
|
|
105880
|
-
var version3 = "0.
|
|
105884
|
+
var version3 = "0.17.0";
|
|
105881
105885
|
|
|
105882
105886
|
// ../tool-server/src/utils/update-checker.ts
|
|
105883
105887
|
var import_update_core = __toESM(require_dist4());
|
|
@@ -114038,6 +114042,27 @@ function sendCommand(api, cmd) {
|
|
|
114038
114042
|
ws.once("open", () => ws.send(payload));
|
|
114039
114043
|
}
|
|
114040
114044
|
}
|
|
114045
|
+
function setPointerVisible(api, show, signal) {
|
|
114046
|
+
return pointerPost(api, { show }, signal);
|
|
114047
|
+
}
|
|
114048
|
+
function setPointerTrail(api, trail, signal) {
|
|
114049
|
+
return pointerPost(api, { trail }, signal);
|
|
114050
|
+
}
|
|
114051
|
+
async function pointerPost(api, body, signal) {
|
|
114052
|
+
try {
|
|
114053
|
+
const res = await fetch(`${api.apiUrl}/api/pointer`, {
|
|
114054
|
+
method: "POST",
|
|
114055
|
+
headers: { "Content-Type": "application/json" },
|
|
114056
|
+
body: JSON.stringify(body),
|
|
114057
|
+
signal
|
|
114058
|
+
});
|
|
114059
|
+
if (!res.ok) return false;
|
|
114060
|
+
const parsed = await res.json().catch(() => null);
|
|
114061
|
+
return parsed?.status === "ok";
|
|
114062
|
+
} catch {
|
|
114063
|
+
return false;
|
|
114064
|
+
}
|
|
114065
|
+
}
|
|
114041
114066
|
async function simulatorPost(toolLabel, api, endpoint, reqBody, signal, fallbackHint) {
|
|
114042
114067
|
let res;
|
|
114043
114068
|
try {
|
|
@@ -126869,6 +126894,7 @@ Use when you need to zoom in or out on a map, image, or zoomable view. Returns {
|
|
|
126869
126894
|
|
|
126870
126895
|
// ../tool-server/src/tools/gesture-rotate/index.ts
|
|
126871
126896
|
init_zod();
|
|
126897
|
+
init_src();
|
|
126872
126898
|
var zodSchema22 = external_exports.object({
|
|
126873
126899
|
udid: external_exports.string().describe("Target device id from `list-devices` (iOS UDID or Android serial)."),
|
|
126874
126900
|
centerX: external_exports.number().describe(
|
|
@@ -126877,13 +126903,33 @@ var zodSchema22 = external_exports.object({
|
|
|
126877
126903
|
centerY: external_exports.number().describe(
|
|
126878
126904
|
"Center of rotation, vertical: normalized 0.0\u20131.0 (fraction of screen height, not pixels)"
|
|
126879
126905
|
),
|
|
126880
|
-
radius: external_exports.number().describe(
|
|
126881
|
-
"Distance from center to each finger: normalized 0.0\u20131.0 (fraction of screen, not pixels). E.g. 0.15 = fingers placed 15% of screen away from center."
|
|
126906
|
+
radius: external_exports.number().optional().describe(
|
|
126907
|
+
"Distance from center to each finger: normalized 0.0\u20131.0 (fraction of screen, not pixels). E.g. 0.15 = fingers placed 15% of screen away from center. One value for both axes, so on a non-square screen the orbit is a physical ellipse \u2014 pass radiusX+radiusY instead for a true circle. Required unless radiusX and radiusY are given."
|
|
126908
|
+
),
|
|
126909
|
+
radiusX: external_exports.number().optional().describe(
|
|
126910
|
+
"Per-axis finger distance, horizontal: normalized 0.0\u20131.0 fraction of screen WIDTH. Give both radiusX and radiusY (they override radius) with radiusX\xB7screenWidth = radiusY\xB7screenHeight for a physically circular orbit \u2014 constant finger separation, no pinch coupled into the turn."
|
|
126911
|
+
),
|
|
126912
|
+
radiusY: external_exports.number().optional().describe(
|
|
126913
|
+
"Per-axis finger distance, vertical: normalized 0.0\u20131.0 fraction of screen HEIGHT. Always paired with radiusX \u2014 see radiusX."
|
|
126882
126914
|
),
|
|
126883
126915
|
startAngle: external_exports.number().describe("Starting angle in degrees (0 = right, 90 = down)"),
|
|
126884
126916
|
endAngle: external_exports.number().describe("Ending angle in degrees. endAngle > startAngle = clockwise."),
|
|
126885
126917
|
durationMs: external_exports.number().optional().describe("Total gesture duration in milliseconds (default 300)")
|
|
126886
|
-
})
|
|
126918
|
+
}).refine((p) => p.radiusX === void 0 === (p.radiusY === void 0), {
|
|
126919
|
+
message: "radiusX and radiusY must be given together (one physical radius, two normalizations)"
|
|
126920
|
+
}).refine((p) => p.radius !== void 0 || p.radiusX !== void 0, {
|
|
126921
|
+
message: "Pass radius, or both radiusX and radiusY."
|
|
126922
|
+
});
|
|
126923
|
+
var inputSchema = {
|
|
126924
|
+
...zodObjectToJsonSchema(zodSchema22),
|
|
126925
|
+
anyOf: [
|
|
126926
|
+
{ required: ["radiusX", "radiusY"] },
|
|
126927
|
+
{
|
|
126928
|
+
required: ["radius"],
|
|
126929
|
+
not: { anyOf: [{ required: ["radiusX"] }, { required: ["radiusY"] }] }
|
|
126930
|
+
}
|
|
126931
|
+
]
|
|
126932
|
+
};
|
|
126887
126933
|
var capability14 = {
|
|
126888
126934
|
apple: { simulator: true, device: true },
|
|
126889
126935
|
appleRemote: { simulator: true },
|
|
@@ -126891,32 +126937,51 @@ var capability14 = {
|
|
|
126891
126937
|
};
|
|
126892
126938
|
var gestureRotateTool = {
|
|
126893
126939
|
id: "gesture-rotate",
|
|
126894
|
-
description: `Send a two-finger circular arc gesture to rotate on-screen content by a specified angle. Two fingers are placed opposite each other at a fixed radius from the center, then swept from startAngle to endAngle degrees. All positions and
|
|
126895
|
-
endAngle > startAngle = clockwise rotation. Typical values: radius 0.15, startAngle 0, endAngle 90 for a 90\xB0 clockwise turn.
|
|
126940
|
+
description: `Send a two-finger circular arc gesture to rotate on-screen content by a specified angle. Two fingers are placed opposite each other at a fixed radius from the center, then swept from startAngle to endAngle degrees. All positions and radii are normalized 0.0\u20131.0 (fractions of screen width/height, not pixels)\u2014same coordinate space as gesture-tap and gesture-swipe.
|
|
126941
|
+
endAngle > startAngle = clockwise rotation. Typical values: radius 0.15, startAngle 0, endAngle 90 for a 90\xB0 clockwise turn. A single radius applies to both axes, so on a non-square screen it traces a physical ellipse (finger separation varies through the turn); pass radiusX+radiusY (fractions of width/height with radiusX\xB7width = radiusY\xB7height) for a physically circular orbit instead.
|
|
126896
126942
|
Auto-generates interpolated frames at ~60fps.
|
|
126897
126943
|
Unlike gesture-pinch which moves fingers linearly to zoom, this orbits fingers in an arc to change orientation.
|
|
126898
126944
|
Use when you need to rotate a map, image picker, or any rotateable UI element. Returns { rotated: true, timestampMs }. Fails if the simulator-server / emulator backend is not reachable for the given device.`,
|
|
126899
126945
|
zodSchema: zodSchema22,
|
|
126946
|
+
inputSchema,
|
|
126900
126947
|
capability: capability14,
|
|
126901
126948
|
services: (params) => ({
|
|
126902
126949
|
simulatorServer: simulatorServerRef(resolveDevice(params.udid))
|
|
126903
126950
|
}),
|
|
126904
|
-
async execute(services, params) {
|
|
126951
|
+
async execute(services, params, ctx) {
|
|
126905
126952
|
const api = services.simulatorServer;
|
|
126906
126953
|
const duration3 = params.durationMs ?? 300;
|
|
126907
126954
|
const steps = Math.max(1, Math.round(duration3 / 16));
|
|
126955
|
+
const radiusX = params.radiusX ?? params.radius;
|
|
126956
|
+
const radiusY = params.radiusY ?? params.radius;
|
|
126908
126957
|
let timestampMs = 0;
|
|
126958
|
+
let lastX1 = 0;
|
|
126959
|
+
let lastY1 = 0;
|
|
126960
|
+
let lastX2 = 0;
|
|
126961
|
+
let lastY2 = 0;
|
|
126909
126962
|
for (let i = 0; i <= steps; i++) {
|
|
126963
|
+
if (ctx?.signal?.aborted) {
|
|
126964
|
+
if (i > 0) sendTouchEvent(api, "Up", lastX1, lastY1, lastX2, lastY2);
|
|
126965
|
+
const err = new Error(
|
|
126966
|
+
`gesture-rotate aborted \u2014 cancelled mid-gesture after ${i} of ${steps + 1} frames`
|
|
126967
|
+
);
|
|
126968
|
+
err.name = "AbortError";
|
|
126969
|
+
throw err;
|
|
126970
|
+
}
|
|
126910
126971
|
const t = i / steps;
|
|
126911
126972
|
const angleDeg = params.startAngle + (params.endAngle - params.startAngle) * t;
|
|
126912
126973
|
const angleRad = angleDeg * Math.PI / 180;
|
|
126913
|
-
const x1 = params.centerX +
|
|
126914
|
-
const y1 = params.centerY +
|
|
126915
|
-
const x2 = params.centerX -
|
|
126916
|
-
const y2 = params.centerY -
|
|
126974
|
+
const x1 = params.centerX + radiusX * Math.cos(angleRad);
|
|
126975
|
+
const y1 = params.centerY + radiusY * Math.sin(angleRad);
|
|
126976
|
+
const x2 = params.centerX - radiusX * Math.cos(angleRad);
|
|
126977
|
+
const y2 = params.centerY - radiusY * Math.sin(angleRad);
|
|
126917
126978
|
const type = i === 0 ? "Down" : i === steps ? "Up" : "Move";
|
|
126918
126979
|
if (i === 0) timestampMs = Date.now();
|
|
126919
126980
|
sendTouchEvent(api, type, x1, y1, x2, y2);
|
|
126981
|
+
lastX1 = x1;
|
|
126982
|
+
lastY1 = y1;
|
|
126983
|
+
lastX2 = x2;
|
|
126984
|
+
lastY2 = y2;
|
|
126920
126985
|
if (i < steps) await sleep(16);
|
|
126921
126986
|
}
|
|
126922
126987
|
return { rotated: true, timestampMs };
|
|
@@ -133994,7 +134059,7 @@ Allowed tools and their args (udid is auto-injected, do NOT include it in args):
|
|
|
133994
134059
|
gesture-drag: { fromX: number, fromY: number, toX: number, toY: number, durationMs?: number } [chromium only]
|
|
133995
134060
|
gesture-custom: { events: [{ type: "Down"|"Move"|"Up", x: number, y: number, x2?: number, y2?: number, delayMs?: number }], interpolate?: number } [ios/android]
|
|
133996
134061
|
gesture-pinch: { centerX: number, centerY: number, startDistance: number, endDistance: number, endCenterX?: number, endCenterY?: number, angle?: number, durationMs?: number } [ios/android]
|
|
133997
|
-
gesture-rotate: { centerX: number, centerY: number, radius
|
|
134062
|
+
gesture-rotate: { centerX: number, centerY: number, radius?: number, radiusX?: number, radiusY?: number, startAngle: number, endAngle: number, durationMs?: number } [ios/android]
|
|
133998
134063
|
button: { button: "home"|"back"|"power"|"volumeUp"|"volumeDown"|"appSwitch"|"actionButton" } [ios/android]
|
|
133999
134064
|
keyboard: { text?: string, key?: string, delayMs?: number } (key pressed after text; TV: text only) [ios/android/chromium/vega/tv]
|
|
134000
134065
|
text supports {{secret:<NAME>}} placeholders, resolved server-side from ARGENT_SECRET_<NAME> env vars (prefix mandatory) \u2014 credentials never enter agent context
|
|
@@ -142986,6 +143051,8 @@ function clearLiveState2(state3) {
|
|
|
142986
143051
|
state3.pendingChild = null;
|
|
142987
143052
|
state3.frameStream = null;
|
|
142988
143053
|
state3.lastFrameStreamError = null;
|
|
143054
|
+
state3.pointerDisable = null;
|
|
143055
|
+
state3.pointerFailed = false;
|
|
142989
143056
|
state3.recordingTimedOut = false;
|
|
142990
143057
|
state3.recordingExitedUnexpectedly = false;
|
|
142991
143058
|
state3.lastExitInfo = null;
|
|
@@ -143039,6 +143106,8 @@ var screenRecordingSessionBlueprint = {
|
|
|
143039
143106
|
trimStatic: true,
|
|
143040
143107
|
framesWritten: 0,
|
|
143041
143108
|
trimmedAnyFrames: false,
|
|
143109
|
+
pointerDisable: null,
|
|
143110
|
+
pointerFailed: false,
|
|
143042
143111
|
wallClockStartMs: null,
|
|
143043
143112
|
wallClockEndMs: null,
|
|
143044
143113
|
timeLimitSeconds: null,
|
|
@@ -143061,6 +143130,12 @@ var screenRecordingSessionBlueprint = {
|
|
|
143061
143130
|
state3.pumpTimer = null;
|
|
143062
143131
|
}
|
|
143063
143132
|
state3.frameStream?.close();
|
|
143133
|
+
if (state3.pointerDisable) {
|
|
143134
|
+
const disable = state3.pointerDisable;
|
|
143135
|
+
state3.pointerDisable = null;
|
|
143136
|
+
await disable().catch(() => {
|
|
143137
|
+
});
|
|
143138
|
+
}
|
|
143064
143139
|
if (state3.pendingChild) {
|
|
143065
143140
|
try {
|
|
143066
143141
|
state3.pendingChild.kill("SIGKILL");
|
|
@@ -143593,6 +143668,12 @@ function startPump(api, stream) {
|
|
|
143593
143668
|
}
|
|
143594
143669
|
}, FRAME_INTERVAL_MS);
|
|
143595
143670
|
}
|
|
143671
|
+
async function disablePointer(api) {
|
|
143672
|
+
const disable = api.pointerDisable;
|
|
143673
|
+
api.pointerDisable = null;
|
|
143674
|
+
if (disable) await disable().catch(() => {
|
|
143675
|
+
});
|
|
143676
|
+
}
|
|
143596
143677
|
function stopPump(api) {
|
|
143597
143678
|
if (api.pumpTimer) {
|
|
143598
143679
|
clearInterval(api.pumpTimer);
|
|
@@ -143687,6 +143768,7 @@ async function startCaptureLocked(api, params) {
|
|
|
143687
143768
|
api.recordingTimedOut = false;
|
|
143688
143769
|
api.recordingExitedUnexpectedly = false;
|
|
143689
143770
|
api.pendingRetrieval = false;
|
|
143771
|
+
api.pointerFailed = false;
|
|
143690
143772
|
api.lastExitInfo = null;
|
|
143691
143773
|
api.lastFrameStreamError = null;
|
|
143692
143774
|
api.outputFile = outputFile;
|
|
@@ -143702,16 +143784,6 @@ async function startCaptureLocked(api, params) {
|
|
|
143702
143784
|
api.timeLimitSeconds = params.timeLimitSeconds;
|
|
143703
143785
|
registerActiveScreenRecording(api.deviceId, api.wallClockStartMs, params.timeLimitSeconds);
|
|
143704
143786
|
startPump(api, stream);
|
|
143705
|
-
api.recordingTimeout = setTimeout(() => {
|
|
143706
|
-
api.recordingTimeout = null;
|
|
143707
|
-
if (api.captureProcess !== child) return;
|
|
143708
|
-
api.recordingTimedOut = true;
|
|
143709
|
-
api.recordingActive = false;
|
|
143710
|
-
api.wallClockEndMs = Date.now();
|
|
143711
|
-
api.pendingRetrieval = true;
|
|
143712
|
-
markScreenRecordingFinalized(api.deviceId, `it hit its ${params.timeLimitSeconds}s time limit`);
|
|
143713
|
-
finalizeCapture(api);
|
|
143714
|
-
}, params.timeLimitSeconds * 1e3);
|
|
143715
143787
|
child.on("exit", (code, signal) => {
|
|
143716
143788
|
if (api.captureProcess !== child) return;
|
|
143717
143789
|
api.lastExitInfo = { code, signal };
|
|
@@ -143726,9 +143798,25 @@ async function startCaptureLocked(api, params) {
|
|
|
143726
143798
|
api.wallClockEndMs = Date.now();
|
|
143727
143799
|
api.pendingRetrieval = true;
|
|
143728
143800
|
stopPump(api);
|
|
143801
|
+
void disablePointer(api);
|
|
143729
143802
|
markScreenRecordingFinalized(api.deviceId, "the recording process exited unexpectedly");
|
|
143730
143803
|
}
|
|
143731
143804
|
});
|
|
143805
|
+
if (params.pointer) {
|
|
143806
|
+
api.pointerDisable = params.pointer.disable;
|
|
143807
|
+
api.pointerFailed = !await params.pointer.enable();
|
|
143808
|
+
}
|
|
143809
|
+
api.recordingTimeout = setTimeout(() => {
|
|
143810
|
+
api.recordingTimeout = null;
|
|
143811
|
+
if (api.captureProcess !== child) return;
|
|
143812
|
+
api.recordingTimedOut = true;
|
|
143813
|
+
api.recordingActive = false;
|
|
143814
|
+
api.wallClockEndMs = Date.now();
|
|
143815
|
+
api.pendingRetrieval = true;
|
|
143816
|
+
markScreenRecordingFinalized(api.deviceId, `it hit its ${params.timeLimitSeconds}s time limit`);
|
|
143817
|
+
finalizeCapture(api);
|
|
143818
|
+
void disablePointer(api);
|
|
143819
|
+
}, params.timeLimitSeconds * 1e3);
|
|
143732
143820
|
return {
|
|
143733
143821
|
status: "recording",
|
|
143734
143822
|
timeLimitSeconds: params.timeLimitSeconds,
|
|
@@ -143795,6 +143883,7 @@ async function stopCapture(api) {
|
|
|
143795
143883
|
const endedEarly = api.recordingTimedOut || api.recordingExitedUnexpectedly;
|
|
143796
143884
|
const streamError = api.frameStream?.error ?? api.lastFrameStreamError ?? null;
|
|
143797
143885
|
const watermarkSkipped = api.watermarkSkipped;
|
|
143886
|
+
const pointerFailed = api.pointerFailed;
|
|
143798
143887
|
let warning;
|
|
143799
143888
|
try {
|
|
143800
143889
|
const child = api.captureProcess;
|
|
@@ -143828,6 +143917,12 @@ async function stopCapture(api) {
|
|
|
143828
143917
|
if (watermarkSkipped) {
|
|
143829
143918
|
warning = [warning, `The watermark was not applied (${watermarkSkipped}).`].filter(Boolean).join(" ");
|
|
143830
143919
|
}
|
|
143920
|
+
if (pointerFailed) {
|
|
143921
|
+
warning = [
|
|
143922
|
+
warning,
|
|
143923
|
+
"The touch visualizer could not be enabled on simulator-server, so touches are not shown in this video."
|
|
143924
|
+
].filter(Boolean).join(" ");
|
|
143925
|
+
}
|
|
143831
143926
|
const size = await statNonEmptyOutput(outputFile, "screen_recording_stop");
|
|
143832
143927
|
const wallClockMs = startedAtMs === null ? null : (api.wallClockEndMs ?? Date.now()) - startedAtMs;
|
|
143833
143928
|
const durationMs = trimStatic ? Math.round(api.framesWritten / OUTPUT_FPS2 * 1e3) : wallClockMs;
|
|
@@ -143846,6 +143941,7 @@ async function stopCapture(api) {
|
|
|
143846
143941
|
throw err;
|
|
143847
143942
|
} finally {
|
|
143848
143943
|
stopPump(api);
|
|
143944
|
+
await disablePointer(api);
|
|
143849
143945
|
api.recordingActive = false;
|
|
143850
143946
|
api.stopPending = false;
|
|
143851
143947
|
api.pendingRetrieval = false;
|
|
@@ -143853,6 +143949,7 @@ async function stopCapture(api) {
|
|
|
143853
143949
|
api.outputFile = null;
|
|
143854
143950
|
api.logoFile = null;
|
|
143855
143951
|
api.watermarkSkipped = null;
|
|
143952
|
+
api.pointerFailed = false;
|
|
143856
143953
|
api.framesWritten = 0;
|
|
143857
143954
|
api.trimmedAnyFrames = false;
|
|
143858
143955
|
api.wallClockStartMs = null;
|
|
@@ -143871,6 +143968,8 @@ async function stopCapture(api) {
|
|
|
143871
143968
|
// ../tool-server/src/tools/screen-recording/screen-recording-start.ts
|
|
143872
143969
|
var DEFAULT_TIME_LIMIT_SECONDS = 180;
|
|
143873
143970
|
var MAX_TIME_LIMIT_SECONDS = 600;
|
|
143971
|
+
var POINTER_TRAIL_LENGTH = 8;
|
|
143972
|
+
var POINTER_REQUEST_TIMEOUT_MS = 2e3;
|
|
143874
143973
|
var zodSchema51 = external_exports.object({
|
|
143875
143974
|
udid: external_exports.string().describe("Target device id from `list-devices` (iOS Simulator UDID or Android serial)."),
|
|
143876
143975
|
timeLimitSeconds: external_exports.number().int().min(1).max(MAX_TIME_LIMIT_SECONDS).optional().describe(
|
|
@@ -143878,6 +143977,9 @@ var zodSchema51 = external_exports.object({
|
|
|
143878
143977
|
),
|
|
143879
143978
|
trimStatic: external_exports.boolean().optional().describe(
|
|
143880
143979
|
"Default true. Collapse stretches where the screen does not change: the first second of each still stretch is kept, then unchanged frames are dropped until something moves again, so a long recording with brief activity comes back short instead of full of dead air. The returned durationMs is the trimmed length; wallClockMs/trimmedMs report what was removed. Set false to keep a faithful real-time recording."
|
|
143980
|
+
),
|
|
143981
|
+
showTouches: external_exports.boolean().optional().describe(
|
|
143982
|
+
"Default true. Draw simulator-server's touch visualizer into the recording: a pulse marks each tap, a comet trail follows swipes and drags, and paired markers show two-finger pinch/rotate, so the video makes clear where every interaction landed. Set false to record the raw screen with no overlay."
|
|
143881
143983
|
)
|
|
143882
143984
|
});
|
|
143883
143985
|
var capability26 = {
|
|
@@ -143890,6 +143992,7 @@ function createScreenRecordingStartTool(registry2) {
|
|
|
143890
143992
|
capability: capability26,
|
|
143891
143993
|
description: `Start recording the device screen to a video file (h264 mp4, 30fps at the device's native resolution).
|
|
143892
143994
|
By default stretches where the screen does not change are trimmed out (see trimStatic), so a long session with only brief activity comes back as a short clip instead of minutes of dead air.
|
|
143995
|
+
By default every tap, swipe, drag, pinch and rotate is drawn into the video as an on-screen touch marker (see showTouches), so the recording shows where each interaction landed.
|
|
143893
143996
|
The recording keeps running across other tool calls (every result carries a reminder) until \`screen-recording-stop\` is called or timeLimitSeconds elapses \u2014 immediately after starting, set yourself a reminder/wakeup for the expected end of the recording so it is never left running.
|
|
143894
143997
|
Use when the user wants a video of an interaction, animation, or app behavior \u2014 for a single still frame use \`screenshot\` instead.
|
|
143895
143998
|
Returns { status: "recording", timeLimitSeconds, outputFile } \u2014 the video is retrieved later by \`screen-recording-stop\`, not by reading outputFile directly.
|
|
@@ -143934,21 +144037,53 @@ Fails if a recording is already running on the device, the device is not booted,
|
|
|
143934
144037
|
}
|
|
143935
144038
|
);
|
|
143936
144039
|
}
|
|
144040
|
+
const showTouches = params.showTouches ?? true;
|
|
144041
|
+
const pointer = showTouches ? makePointerControl(simulator) : void 0;
|
|
143937
144042
|
return startCapture(api, {
|
|
143938
144043
|
streamUrl,
|
|
143939
144044
|
timeLimitSeconds,
|
|
143940
144045
|
watermark: isFeatureEnabled("video-watermark"),
|
|
143941
|
-
trimStatic: params.trimStatic ?? true
|
|
144046
|
+
trimStatic: params.trimStatic ?? true,
|
|
144047
|
+
pointer
|
|
143942
144048
|
});
|
|
143943
144049
|
}
|
|
143944
144050
|
};
|
|
143945
144051
|
}
|
|
144052
|
+
function makePointerControl(simulator) {
|
|
144053
|
+
let enabling = null;
|
|
144054
|
+
return {
|
|
144055
|
+
async enable() {
|
|
144056
|
+
const run2 = (async () => {
|
|
144057
|
+
await setPointerTrail(
|
|
144058
|
+
simulator,
|
|
144059
|
+
POINTER_TRAIL_LENGTH,
|
|
144060
|
+
AbortSignal.timeout(POINTER_REQUEST_TIMEOUT_MS)
|
|
144061
|
+
);
|
|
144062
|
+
return setPointerVisible(simulator, true, AbortSignal.timeout(POINTER_REQUEST_TIMEOUT_MS));
|
|
144063
|
+
})();
|
|
144064
|
+
enabling = run2;
|
|
144065
|
+
try {
|
|
144066
|
+
return await run2;
|
|
144067
|
+
} finally {
|
|
144068
|
+
if (enabling === run2) enabling = null;
|
|
144069
|
+
}
|
|
144070
|
+
},
|
|
144071
|
+
async disable() {
|
|
144072
|
+
const pending = enabling;
|
|
144073
|
+
if (pending) await pending.catch(() => {
|
|
144074
|
+
});
|
|
144075
|
+
await setPointerVisible(simulator, false, AbortSignal.timeout(POINTER_REQUEST_TIMEOUT_MS));
|
|
144076
|
+
}
|
|
144077
|
+
};
|
|
144078
|
+
}
|
|
143946
144079
|
|
|
143947
144080
|
// ../tool-server/src/tools/screen-recording/screen-recording-stop.ts
|
|
144081
|
+
var import_node_path20 = require("node:path");
|
|
143948
144082
|
init_zod();
|
|
143949
144083
|
var zodSchema52 = external_exports.object({
|
|
143950
144084
|
udid: external_exports.string().describe("Target device id from `list-devices` (iOS Simulator UDID or Android serial).")
|
|
143951
144085
|
});
|
|
144086
|
+
var RECORDINGS_DIR = ".argent/recordings";
|
|
143952
144087
|
var capability27 = {
|
|
143953
144088
|
apple: { simulator: true },
|
|
143954
144089
|
android: { emulator: true, device: true, unknown: true }
|
|
@@ -143972,7 +144107,13 @@ Fails if no recording (running or finished-but-unretrieved) exists for the given
|
|
|
143972
144107
|
assertSupported("screen-recording-stop", capability27, device);
|
|
143973
144108
|
const stopped = await stopCapture(api);
|
|
143974
144109
|
const artifacts = requireArtifacts(ctx);
|
|
143975
|
-
const video = await artifacts.register(stopped.outputFile, {
|
|
144110
|
+
const video = await artifacts.register(stopped.outputFile, {
|
|
144111
|
+
mimeType: "video/mp4",
|
|
144112
|
+
// Drop the internal `argent-` temp prefix so the saved file reads cleanly
|
|
144113
|
+
// as `.argent/recordings/screen-recording-<device>-<ts>.mp4`.
|
|
144114
|
+
filename: (0, import_node_path20.basename)(stopped.outputFile).replace(/^argent-/, ""),
|
|
144115
|
+
saveDir: RECORDINGS_DIR
|
|
144116
|
+
});
|
|
143976
144117
|
const result = { video, durationMs: stopped.durationMs };
|
|
143977
144118
|
if (stopped.wallClockMs !== void 0) result.wallClockMs = stopped.wallClockMs;
|
|
143978
144119
|
if (stopped.trimmedMs !== void 0) result.trimmedMs = stopped.trimmedMs;
|
|
@@ -145848,6 +145989,123 @@ var fs39 = __toESM(require("node:fs/promises"));
|
|
|
145848
145989
|
init_src();
|
|
145849
145990
|
var import_yaml = __toESM(require_dist5());
|
|
145850
145991
|
init_src();
|
|
145992
|
+
|
|
145993
|
+
// ../tool-server/src/tools/flows/flow-pinch-geometry.ts
|
|
145994
|
+
var PINCH_RATIO_PER_GESTURE = 4;
|
|
145995
|
+
var PINCH_SETTLE_MS = 250;
|
|
145996
|
+
var TARGET_START_FRACTION = 0.82;
|
|
145997
|
+
var SCREEN_EDGE_INSET = 0.02;
|
|
145998
|
+
var MIN_VIABLE_TRAVEL = 0.03;
|
|
145999
|
+
function systemEdgeGuards(device) {
|
|
146000
|
+
return device.platform === "android" ? { left: 0.13, right: 0.13, top: 0.08, bottom: 0.08 } : { left: 0.08, right: 0.08, top: 0.08, bottom: 0.08 };
|
|
146001
|
+
}
|
|
146002
|
+
function decomposePinch(scale) {
|
|
146003
|
+
const n = Math.max(
|
|
146004
|
+
1,
|
|
146005
|
+
Math.ceil(Math.abs(Math.log(scale)) / Math.log(PINCH_RATIO_PER_GESTURE) - 1e-9)
|
|
146006
|
+
);
|
|
146007
|
+
return { n, per: scale ** (1 / n) };
|
|
146008
|
+
}
|
|
146009
|
+
function clamp2(v, lo, hi) {
|
|
146010
|
+
return v < lo ? lo : v > hi ? hi : v;
|
|
146011
|
+
}
|
|
146012
|
+
function buildAxisCandidate(input) {
|
|
146013
|
+
const { angle, center, targetSpan, per, guards } = input;
|
|
146014
|
+
const c = angle === 0 ? center.x : center.y;
|
|
146015
|
+
const gLow = angle === 0 ? guards.left : guards.top;
|
|
146016
|
+
const gHigh = angle === 0 ? guards.right : guards.bottom;
|
|
146017
|
+
const p = angle === 0 ? center.y : center.x;
|
|
146018
|
+
const pLow = angle === 0 ? guards.top : guards.left;
|
|
146019
|
+
const pHigh = angle === 0 ? guards.bottom : guards.right;
|
|
146020
|
+
const screenEndSpan = 1 - 2 * SCREEN_EDGE_INSET;
|
|
146021
|
+
const edgeSafeStartSpan = 2 * Math.max(0, Math.min(c - gLow, 1 - gHigh - c));
|
|
146022
|
+
const insetStartSpan = 2 * Math.max(0, Math.min(c - SCREEN_EDGE_INSET, 1 - SCREEN_EDGE_INSET - c));
|
|
146023
|
+
const screenStartLimit = edgeSafeStartSpan > 0 ? Math.min(screenEndSpan, edgeSafeStartSpan) : Math.min(screenEndSpan, insetStartSpan);
|
|
146024
|
+
const startLimit = targetSpan !== void 0 ? Math.min(screenStartLimit, targetSpan * TARGET_START_FRACTION) : screenStartLimit;
|
|
146025
|
+
const start2 = per > 1 ? Math.min(startLimit, screenEndSpan / per) : startLimit;
|
|
146026
|
+
const end = Math.min(screenEndSpan, start2 * per);
|
|
146027
|
+
const travel = Math.abs(end - start2);
|
|
146028
|
+
if (!(travel > 0)) return void 0;
|
|
146029
|
+
const endCenter = clamp2(c, SCREEN_EDGE_INSET + end / 2, 1 - SCREEN_EDGE_INSET - end / 2);
|
|
146030
|
+
const downLow = c - start2 / 2;
|
|
146031
|
+
const downHigh = c + start2 / 2;
|
|
146032
|
+
const axisSafe = downLow >= gLow && downHigh <= 1 - gHigh;
|
|
146033
|
+
const perpSafe = p >= pLow && p <= 1 - pHigh;
|
|
146034
|
+
const clearance = Math.min(downLow - gLow, 1 - gHigh - downHigh, p - pLow, 1 - pHigh - p);
|
|
146035
|
+
return {
|
|
146036
|
+
angle,
|
|
146037
|
+
start: start2,
|
|
146038
|
+
end,
|
|
146039
|
+
endCenter,
|
|
146040
|
+
travel,
|
|
146041
|
+
viable: travel >= MIN_VIABLE_TRAVEL,
|
|
146042
|
+
axisSafe,
|
|
146043
|
+
fullyEdgeSafe: axisSafe && perpSafe,
|
|
146044
|
+
clearance
|
|
146045
|
+
};
|
|
146046
|
+
}
|
|
146047
|
+
function selectPinchCandidate(candidates) {
|
|
146048
|
+
if (candidates.length === 0) return void 0;
|
|
146049
|
+
const rank2 = (c) => c.fullyEdgeSafe ? 0 : c.axisSafe ? 1 : 2;
|
|
146050
|
+
return [...candidates].sort((a, b) => {
|
|
146051
|
+
const byViable = Number(b.viable) - Number(a.viable);
|
|
146052
|
+
if (byViable !== 0) return byViable;
|
|
146053
|
+
const byRank = rank2(a) - rank2(b);
|
|
146054
|
+
if (byRank !== 0) return byRank;
|
|
146055
|
+
if (a.fullyEdgeSafe && b.fullyEdgeSafe) return b.travel - a.travel;
|
|
146056
|
+
return b.clearance - a.clearance || b.travel - a.travel;
|
|
146057
|
+
})[0];
|
|
146058
|
+
}
|
|
146059
|
+
|
|
146060
|
+
// ../tool-server/src/tools/flows/flow-rotate-geometry.ts
|
|
146061
|
+
var ROTATE_MS_PER_90_DEG = 300;
|
|
146062
|
+
var MAX_DERIVED_ROTATE_MS = 1e4;
|
|
146063
|
+
var MAX_ROTATE_BY_DEG = MAX_DERIVED_ROTATE_MS / ROTATE_MS_PER_90_DEG * 90;
|
|
146064
|
+
function deriveRotateDurationMs(by) {
|
|
146065
|
+
return Math.round(ROTATE_MS_PER_90_DEG * Math.max(1, Math.abs(by) / 90));
|
|
146066
|
+
}
|
|
146067
|
+
function buildRotateCandidate(input) {
|
|
146068
|
+
const { startAngle, center, targetSpan, guards, aspect } = input;
|
|
146069
|
+
const xClear = Math.min(center.x - SCREEN_EDGE_INSET, 1 - SCREEN_EDGE_INSET - center.x);
|
|
146070
|
+
const yClear = Math.min(center.y - SCREEN_EDGE_INSET, 1 - SCREEN_EDGE_INSET - center.y);
|
|
146071
|
+
const screenRx = Math.min(xClear, yClear / aspect);
|
|
146072
|
+
const targetRx = targetSpan === void 0 ? Infinity : startAngle === 0 ? targetSpan * TARGET_START_FRACTION / 2 : targetSpan * TARGET_START_FRACTION / 2 / aspect;
|
|
146073
|
+
const radiusX = Math.min(screenRx, targetRx);
|
|
146074
|
+
if (!(radiusX > 0)) return void 0;
|
|
146075
|
+
const radiusY = radiusX * aspect;
|
|
146076
|
+
const c = startAngle === 0 ? center.x : center.y;
|
|
146077
|
+
const half = startAngle === 0 ? radiusX : radiusY;
|
|
146078
|
+
const gLow = startAngle === 0 ? guards.left : guards.top;
|
|
146079
|
+
const gHigh = startAngle === 0 ? guards.right : guards.bottom;
|
|
146080
|
+
const p = startAngle === 0 ? center.y : center.x;
|
|
146081
|
+
const pLow = startAngle === 0 ? guards.top : guards.left;
|
|
146082
|
+
const pHigh = startAngle === 0 ? guards.bottom : guards.right;
|
|
146083
|
+
const downLow = c - half;
|
|
146084
|
+
const downHigh = c + half;
|
|
146085
|
+
const axisSafe = downLow >= gLow && downHigh <= 1 - gHigh;
|
|
146086
|
+
const perpSafe = p >= pLow && p <= 1 - pHigh;
|
|
146087
|
+
const clearance = Math.min(downLow - gLow, 1 - gHigh - downHigh, p - pLow, 1 - pHigh - p);
|
|
146088
|
+
return {
|
|
146089
|
+
startAngle,
|
|
146090
|
+
radiusX,
|
|
146091
|
+
radiusY,
|
|
146092
|
+
axisSafe,
|
|
146093
|
+
fullyEdgeSafe: axisSafe && perpSafe,
|
|
146094
|
+
clearance
|
|
146095
|
+
};
|
|
146096
|
+
}
|
|
146097
|
+
function selectRotateCandidate(candidates) {
|
|
146098
|
+
if (candidates.length === 0) return void 0;
|
|
146099
|
+
const rank2 = (c) => c.fullyEdgeSafe ? 0 : c.axisSafe ? 1 : 2;
|
|
146100
|
+
return [...candidates].sort((a, b) => {
|
|
146101
|
+
const byRank = rank2(a) - rank2(b);
|
|
146102
|
+
if (byRank !== 0) return byRank;
|
|
146103
|
+
if (a.fullyEdgeSafe && b.fullyEdgeSafe) return b.radiusX - a.radiusX;
|
|
146104
|
+
return b.clearance - a.clearance || b.radiusX - a.radiusX;
|
|
146105
|
+
})[0];
|
|
146106
|
+
}
|
|
146107
|
+
|
|
146108
|
+
// ../tool-server/src/tools/flows/flow-utils.ts
|
|
145851
146109
|
var FLOWS_DIR_NAME = path29.join(".argent", "flows");
|
|
145852
146110
|
var activeFlowName = null;
|
|
145853
146111
|
var activeProjectRoot = null;
|
|
@@ -146157,6 +146415,10 @@ function toYamlStep(step) {
|
|
|
146157
146415
|
return {
|
|
146158
146416
|
pinch: step.selector ? { on: selectorToYaml(step.selector), scale: step.scale } : { scale: step.scale }
|
|
146159
146417
|
};
|
|
146418
|
+
case "rotate":
|
|
146419
|
+
return {
|
|
146420
|
+
rotate: step.selector ? { on: selectorToYaml(step.selector), by: step.by } : { by: step.by }
|
|
146421
|
+
};
|
|
146160
146422
|
case "snapshot":
|
|
146161
146423
|
return step.maxMismatch === void 0 ? { snapshot: step.name } : { snapshot: { name: step.name, maxMismatch: step.maxMismatch } };
|
|
146162
146424
|
case "tool":
|
|
@@ -146430,6 +146692,7 @@ var STEP_DIRECTIVE_KEYS = [
|
|
|
146430
146692
|
"wait",
|
|
146431
146693
|
"scroll-to",
|
|
146432
146694
|
"pinch",
|
|
146695
|
+
"rotate",
|
|
146433
146696
|
"snapshot"
|
|
146434
146697
|
];
|
|
146435
146698
|
function parseTapTimes(raw, entry) {
|
|
@@ -146548,6 +146811,31 @@ function parsePinch(body, entry) {
|
|
|
146548
146811
|
if (obj.on !== void 0) step.selector = parseSelector(obj.on, "pinch.on");
|
|
146549
146812
|
return step;
|
|
146550
146813
|
}
|
|
146814
|
+
function parseRotate(body, entry) {
|
|
146815
|
+
if (body === null || typeof body !== "object") {
|
|
146816
|
+
badEntry(
|
|
146817
|
+
entry,
|
|
146818
|
+
'rotate takes an options map \u2014 e.g. rotate: { on: "Map", by: 90 } (an angle is required)'
|
|
146819
|
+
);
|
|
146820
|
+
}
|
|
146821
|
+
const obj = body;
|
|
146822
|
+
if (obj.text !== void 0 || obj.id !== void 0 || obj.identifier !== void 0 || obj.role !== void 0) {
|
|
146823
|
+
badEntry(entry, 'rotate takes a nested selector \u2014 e.g. rotate: { on: "Map", by: 90 }');
|
|
146824
|
+
}
|
|
146825
|
+
rejectUnknownKeys(entry, obj, ["on", "by"], "rotate");
|
|
146826
|
+
if (typeof obj.by !== "number" || !Number.isFinite(obj.by) || obj.by === 0) {
|
|
146827
|
+
badEntry(entry, "rotate.by must be a finite non-zero number of degrees (+CW, \u2212CCW)");
|
|
146828
|
+
}
|
|
146829
|
+
if (Math.abs(obj.by) > MAX_ROTATE_BY_DEG) {
|
|
146830
|
+
badEntry(
|
|
146831
|
+
entry,
|
|
146832
|
+
`rotate.by must be within \xB1${MAX_ROTATE_BY_DEG}\xB0 \u2014 one continuous gesture at ~300\xB0/s (10 s max)`
|
|
146833
|
+
);
|
|
146834
|
+
}
|
|
146835
|
+
const step = { kind: "rotate", by: obj.by };
|
|
146836
|
+
if (obj.on !== void 0) step.selector = parseSelector(obj.on, "rotate.on");
|
|
146837
|
+
return step;
|
|
146838
|
+
}
|
|
146551
146839
|
function parseWhenCondition(raw) {
|
|
146552
146840
|
const conditionKeys = `${WAIT_CONDITIONS.join(", ")}, platform`;
|
|
146553
146841
|
if (raw === null || typeof raw !== "object") {
|
|
@@ -146716,6 +147004,7 @@ function fromYamlStep(raw, whenDepth = 0) {
|
|
|
146716
147004
|
return step;
|
|
146717
147005
|
}
|
|
146718
147006
|
if ("pinch" in raw) return parsePinch(raw.pinch, raw);
|
|
147007
|
+
if ("rotate" in raw) return parseRotate(raw.rotate, raw);
|
|
146719
147008
|
if ("snapshot" in raw) {
|
|
146720
147009
|
const body = raw.snapshot;
|
|
146721
147010
|
if (body !== null && typeof body === "object" && !Array.isArray(body)) {
|
|
@@ -147120,7 +147409,7 @@ function projectIosNode(node, screenW, screenH) {
|
|
|
147120
147409
|
scrolls: role === "AXScrollArea"
|
|
147121
147410
|
};
|
|
147122
147411
|
}
|
|
147123
|
-
function
|
|
147412
|
+
function adaptFullHierarchy(raw) {
|
|
147124
147413
|
const windows = typeof raw === "object" && raw !== null && Array.isArray(raw.windows) ? raw.windows.map(asViewNode).filter((n) => n !== null) : [];
|
|
147125
147414
|
let screenW = 0;
|
|
147126
147415
|
let screenH = 0;
|
|
@@ -147137,11 +147426,12 @@ function adaptFullHierarchyToDescribeResult(raw) {
|
|
|
147137
147426
|
flattenHoisting(win, (n) => projectIosNode(n, screenW, screenH), children);
|
|
147138
147427
|
}
|
|
147139
147428
|
}
|
|
147140
|
-
|
|
147429
|
+
const tree = parseDescribeResult({
|
|
147141
147430
|
role: "AXGroup",
|
|
147142
147431
|
frame: { x: 0, y: 0, width: 1, height: 1 },
|
|
147143
147432
|
children
|
|
147144
147433
|
});
|
|
147434
|
+
return screenW > 0 && screenH > 0 ? { tree, screen: { width: screenW, height: screenH } } : { tree };
|
|
147145
147435
|
}
|
|
147146
147436
|
var FULL_HIERARCHY_FIELDS = [
|
|
147147
147437
|
"className",
|
|
@@ -147183,7 +147473,8 @@ async function queryFullHierarchyTree(registry2, device) {
|
|
|
147183
147473
|
if (rawResult.error) {
|
|
147184
147474
|
throw new Error(`getFullHierarchy failed for ${target.bundleId}: ${rawResult.error}`);
|
|
147185
147475
|
}
|
|
147186
|
-
|
|
147476
|
+
const { tree, screen } = adaptFullHierarchy(rawResult);
|
|
147477
|
+
return { tree, source: "native-devtools", ...screen ? { screen } : {} };
|
|
147187
147478
|
}
|
|
147188
147479
|
function errMsg(err) {
|
|
147189
147480
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -147306,7 +147597,11 @@ async function queryAndroidFullHierarchy(registry2, device) {
|
|
|
147306
147597
|
devtools.getScreenSize()
|
|
147307
147598
|
]);
|
|
147308
147599
|
const tree = adaptFullAndroidHierarchyToDescribeResult(xml, size.width, size.height);
|
|
147309
|
-
return {
|
|
147600
|
+
return {
|
|
147601
|
+
tree,
|
|
147602
|
+
source: "android-devtools",
|
|
147603
|
+
...size.width > 0 && size.height > 0 ? { screen: { width: size.width, height: size.height } } : {}
|
|
147604
|
+
};
|
|
147310
147605
|
}
|
|
147311
147606
|
|
|
147312
147607
|
// ../tool-server/src/tools/flows/flow-chromium-tree.ts
|
|
@@ -147614,6 +147909,8 @@ You can still edit the .yaml file directly afterwards to remove or reorder steps
|
|
|
147614
147909
|
return `${n}. scroll-to: ${selectorLabel(step.target)} (${step.direction})`;
|
|
147615
147910
|
case "pinch":
|
|
147616
147911
|
return `${n}. pinch: scale ${step.scale}${step.selector ? ` on ${selectorLabel(step.selector)}` : ""}`;
|
|
147912
|
+
case "rotate":
|
|
147913
|
+
return `${n}. rotate: by ${step.by}\xB0${step.selector ? ` on ${selectorLabel(step.selector)}` : ""}`;
|
|
147617
147914
|
case "snapshot":
|
|
147618
147915
|
return `${n}. snapshot: ${step.name}`;
|
|
147619
147916
|
case "tool":
|
|
@@ -147640,73 +147937,6 @@ var fs45 = __toESM(require("node:fs/promises"));
|
|
|
147640
147937
|
var path33 = __toESM(require("node:path"));
|
|
147641
147938
|
init_src();
|
|
147642
147939
|
|
|
147643
|
-
// ../tool-server/src/tools/flows/flow-pinch-geometry.ts
|
|
147644
|
-
var PINCH_RATIO_PER_GESTURE = 4;
|
|
147645
|
-
var PINCH_SETTLE_MS = 250;
|
|
147646
|
-
var TARGET_START_FRACTION = 0.82;
|
|
147647
|
-
var SCREEN_EDGE_INSET = 0.02;
|
|
147648
|
-
var MIN_VIABLE_TRAVEL = 0.03;
|
|
147649
|
-
function systemEdgeGuards(device) {
|
|
147650
|
-
return device.platform === "android" ? { left: 0.13, right: 0.13, top: 0.08, bottom: 0.08 } : { left: 0.08, right: 0.08, top: 0.08, bottom: 0.08 };
|
|
147651
|
-
}
|
|
147652
|
-
function decomposePinch(scale) {
|
|
147653
|
-
const n = Math.max(
|
|
147654
|
-
1,
|
|
147655
|
-
Math.ceil(Math.abs(Math.log(scale)) / Math.log(PINCH_RATIO_PER_GESTURE) - 1e-9)
|
|
147656
|
-
);
|
|
147657
|
-
return { n, per: scale ** (1 / n) };
|
|
147658
|
-
}
|
|
147659
|
-
function clamp2(v, lo, hi) {
|
|
147660
|
-
return v < lo ? lo : v > hi ? hi : v;
|
|
147661
|
-
}
|
|
147662
|
-
function buildAxisCandidate(input) {
|
|
147663
|
-
const { angle, center, targetSpan, per, guards } = input;
|
|
147664
|
-
const c = angle === 0 ? center.x : center.y;
|
|
147665
|
-
const gLow = angle === 0 ? guards.left : guards.top;
|
|
147666
|
-
const gHigh = angle === 0 ? guards.right : guards.bottom;
|
|
147667
|
-
const p = angle === 0 ? center.y : center.x;
|
|
147668
|
-
const pLow = angle === 0 ? guards.top : guards.left;
|
|
147669
|
-
const pHigh = angle === 0 ? guards.bottom : guards.right;
|
|
147670
|
-
const screenEndSpan = 1 - 2 * SCREEN_EDGE_INSET;
|
|
147671
|
-
const edgeSafeStartSpan = 2 * Math.max(0, Math.min(c - gLow, 1 - gHigh - c));
|
|
147672
|
-
const insetStartSpan = 2 * Math.max(0, Math.min(c - SCREEN_EDGE_INSET, 1 - SCREEN_EDGE_INSET - c));
|
|
147673
|
-
const screenStartLimit = edgeSafeStartSpan > 0 ? Math.min(screenEndSpan, edgeSafeStartSpan) : Math.min(screenEndSpan, insetStartSpan);
|
|
147674
|
-
const startLimit = targetSpan !== void 0 ? Math.min(screenStartLimit, targetSpan * TARGET_START_FRACTION) : screenStartLimit;
|
|
147675
|
-
const start2 = per > 1 ? Math.min(startLimit, screenEndSpan / per) : startLimit;
|
|
147676
|
-
const end = Math.min(screenEndSpan, start2 * per);
|
|
147677
|
-
const travel = Math.abs(end - start2);
|
|
147678
|
-
if (!(travel > 0)) return void 0;
|
|
147679
|
-
const endCenter = clamp2(c, SCREEN_EDGE_INSET + end / 2, 1 - SCREEN_EDGE_INSET - end / 2);
|
|
147680
|
-
const downLow = c - start2 / 2;
|
|
147681
|
-
const downHigh = c + start2 / 2;
|
|
147682
|
-
const axisSafe = downLow >= gLow && downHigh <= 1 - gHigh;
|
|
147683
|
-
const perpSafe = p >= pLow && p <= 1 - pHigh;
|
|
147684
|
-
const clearance = Math.min(downLow - gLow, 1 - gHigh - downHigh, p - pLow, 1 - pHigh - p);
|
|
147685
|
-
return {
|
|
147686
|
-
angle,
|
|
147687
|
-
start: start2,
|
|
147688
|
-
end,
|
|
147689
|
-
endCenter,
|
|
147690
|
-
travel,
|
|
147691
|
-
viable: travel >= MIN_VIABLE_TRAVEL,
|
|
147692
|
-
axisSafe,
|
|
147693
|
-
fullyEdgeSafe: axisSafe && perpSafe,
|
|
147694
|
-
clearance
|
|
147695
|
-
};
|
|
147696
|
-
}
|
|
147697
|
-
function selectPinchCandidate(candidates) {
|
|
147698
|
-
if (candidates.length === 0) return void 0;
|
|
147699
|
-
const rank2 = (c) => c.fullyEdgeSafe ? 0 : c.axisSafe ? 1 : 2;
|
|
147700
|
-
return [...candidates].sort((a, b) => {
|
|
147701
|
-
const byViable = Number(b.viable) - Number(a.viable);
|
|
147702
|
-
if (byViable !== 0) return byViable;
|
|
147703
|
-
const byRank = rank2(a) - rank2(b);
|
|
147704
|
-
if (byRank !== 0) return byRank;
|
|
147705
|
-
if (a.fullyEdgeSafe && b.fullyEdgeSafe) return b.travel - a.travel;
|
|
147706
|
-
return b.clearance - a.clearance || b.travel - a.travel;
|
|
147707
|
-
})[0];
|
|
147708
|
-
}
|
|
147709
|
-
|
|
147710
147940
|
// ../tool-server/src/tools/flows/flow-actions.ts
|
|
147711
147941
|
var ABORTED_OUTCOME = {
|
|
147712
147942
|
ok: false,
|
|
@@ -147920,16 +148150,16 @@ function offscreenHint(sel) {
|
|
|
147920
148150
|
return `no visible element matched selector ${describeSelector(sel)} \u2014 if it is off-screen, add a scroll-to step before this one`;
|
|
147921
148151
|
}
|
|
147922
148152
|
async function runDirective(env, step) {
|
|
147923
|
-
if (env.device.platform === "vega" && (step.kind === "tap" || step.kind === "long-press" || step.kind === "type" || step.kind === "scroll-to" || step.kind === "pinch")) {
|
|
148153
|
+
if (env.device.platform === "vega" && (step.kind === "tap" || step.kind === "long-press" || step.kind === "type" || step.kind === "scroll-to" || step.kind === "pinch" || step.kind === "rotate")) {
|
|
147924
148154
|
return {
|
|
147925
148155
|
ok: false,
|
|
147926
148156
|
reason: `${step.kind} is a touch directive and Vega is remote-driven \u2014 move focus with \`tool: tv-remote\` steps (and type via \`tool: keyboard\`) instead`
|
|
147927
148157
|
};
|
|
147928
148158
|
}
|
|
147929
|
-
if (step.kind === "pinch" && env.device.platform === "chromium") {
|
|
148159
|
+
if ((step.kind === "pinch" || step.kind === "rotate") && env.device.platform === "chromium") {
|
|
147930
148160
|
return {
|
|
147931
148161
|
ok: false,
|
|
147932
|
-
reason: "pinch is unsupported on chromium \u2014 desktop apps have no uniform pinch-zoom mapping (they zoom via ctrl+wheel or their own controls); drive the app's zoom UI with tap/keyboard instead"
|
|
148162
|
+
reason: step.kind === "pinch" ? "pinch is unsupported on chromium \u2014 desktop apps have no uniform pinch-zoom mapping (they zoom via ctrl+wheel or their own controls); drive the app's zoom UI with tap/keyboard instead" : "rotate is unsupported on chromium \u2014 desktop apps have no rotate-gesture idiom; drive the app's rotate controls with tap/keyboard instead"
|
|
147933
148163
|
};
|
|
147934
148164
|
}
|
|
147935
148165
|
switch (step.kind) {
|
|
@@ -147950,6 +148180,8 @@ async function runDirective(env, step) {
|
|
|
147950
148180
|
}
|
|
147951
148181
|
case "pinch":
|
|
147952
148182
|
return runPinch(env, step);
|
|
148183
|
+
case "rotate":
|
|
148184
|
+
return runRotate(env, step);
|
|
147953
148185
|
}
|
|
147954
148186
|
}
|
|
147955
148187
|
async function resolveTargetPoint(env, target) {
|
|
@@ -148039,6 +148271,66 @@ async function runPinch(env, step) {
|
|
|
148039
148271
|
}
|
|
148040
148272
|
return { ok: true };
|
|
148041
148273
|
}
|
|
148274
|
+
async function fetchScreenAspect(env) {
|
|
148275
|
+
try {
|
|
148276
|
+
const { screen } = await fetchFlowTree(env.registry, env.device);
|
|
148277
|
+
return screen && screen.width > 0 && screen.height > 0 ? screen.width / screen.height : void 0;
|
|
148278
|
+
} catch {
|
|
148279
|
+
return void 0;
|
|
148280
|
+
}
|
|
148281
|
+
}
|
|
148282
|
+
async function runRotate(env, step) {
|
|
148283
|
+
let center = { x: 0.5, y: 0.5 };
|
|
148284
|
+
let frame;
|
|
148285
|
+
if (step.selector) {
|
|
148286
|
+
const resolved = await waitForFrame(env, step.selector);
|
|
148287
|
+
if (resolved === "aborted") return ABORTED_OUTCOME;
|
|
148288
|
+
if (!resolved) return { ok: false, reason: offscreenHint(step.selector) };
|
|
148289
|
+
frame = resolved;
|
|
148290
|
+
center = getDescribeTapPoint(resolved);
|
|
148291
|
+
}
|
|
148292
|
+
const aspect = await fetchScreenAspect(env);
|
|
148293
|
+
const guards = systemEdgeGuards(env.device);
|
|
148294
|
+
const candidates = [
|
|
148295
|
+
buildRotateCandidate({
|
|
148296
|
+
startAngle: 0,
|
|
148297
|
+
center,
|
|
148298
|
+
targetSpan: frame?.width,
|
|
148299
|
+
guards,
|
|
148300
|
+
aspect: aspect ?? 1
|
|
148301
|
+
}),
|
|
148302
|
+
buildRotateCandidate({
|
|
148303
|
+
startAngle: 90,
|
|
148304
|
+
center,
|
|
148305
|
+
targetSpan: frame?.height,
|
|
148306
|
+
guards,
|
|
148307
|
+
aspect: aspect ?? 1
|
|
148308
|
+
})
|
|
148309
|
+
].filter((c) => c !== void 0);
|
|
148310
|
+
const selected = selectRotateCandidate(candidates);
|
|
148311
|
+
if (!selected) {
|
|
148312
|
+
return {
|
|
148313
|
+
ok: false,
|
|
148314
|
+
reason: `rotate found no on-screen orbit radius around (${center.x}, ${center.y})`
|
|
148315
|
+
};
|
|
148316
|
+
}
|
|
148317
|
+
if (env.signal?.aborted) return ABORTED_OUTCOME;
|
|
148318
|
+
try {
|
|
148319
|
+
await invokeOnDevice(env, "gesture-rotate", {
|
|
148320
|
+
centerX: center.x,
|
|
148321
|
+
centerY: center.y,
|
|
148322
|
+
...aspect === void 0 ? { radius: selected.radiusX } : { radiusX: selected.radiusX, radiusY: selected.radiusY },
|
|
148323
|
+
startAngle: selected.startAngle,
|
|
148324
|
+
// endAngle > startAngle = clockwise in the tool, matching +by.
|
|
148325
|
+
endAngle: selected.startAngle + step.by,
|
|
148326
|
+
durationMs: deriveRotateDurationMs(step.by)
|
|
148327
|
+
});
|
|
148328
|
+
} catch (err) {
|
|
148329
|
+
if (env.signal?.aborted) return ABORTED_OUTCOME;
|
|
148330
|
+
throw err;
|
|
148331
|
+
}
|
|
148332
|
+
return { ok: true };
|
|
148333
|
+
}
|
|
148042
148334
|
async function runType(env, step) {
|
|
148043
148335
|
const frame = await waitForFrame(env, step.into);
|
|
148044
148336
|
if (frame === "aborted") return ABORTED_OUTCOME;
|
|
@@ -150499,7 +150791,9 @@ ready; \`tool\` calls dispatch through the registry; \`tap\`/\`long-press\`/\`ty
|
|
|
150499
150791
|
element and act on it (\`tap: { on, times: 2 }\` double-taps; \`long-press: { on, duration }\` presses and
|
|
150500
150792
|
holds; \`tap\`/\`long-press\` alternatively take a raw normalized point \u2014 bare \`{ x, y }\` or \`on: { x, y }\`);
|
|
150501
150793
|
\`scroll-to\` scrolls (momentum-free) until a target is visible; \`pinch\` zooms
|
|
150502
|
-
(\`pinch: { on?, scale }\` \u2014 scale > 1 in, < 1 out; screen center when \`on\` is omitted); \`
|
|
150794
|
+
(\`pinch: { on?, scale }\` \u2014 scale > 1 in, < 1 out; screen center when \`on\` is omitted); \`rotate\` is the
|
|
150795
|
+
two-finger rotation gesture (\`rotate: { on?, by }\` \u2014 degrees, + clockwise, within \xB13000\xB0; screen center
|
|
150796
|
+
when \`on\` is omitted; distinct from the \`rotate\` tool, which changes device orientation); \`await\` waits
|
|
150503
150797
|
for a UI condition; \`wait\` pauses for a fixed number of milliseconds; \`assert\` checks one now; \`snapshot\`
|
|
150504
150798
|
diffs a screenshot against a stored baseline (a missing baseline fails the step \u2014 set updateBaselines
|
|
150505
150799
|
to adopt the current screen); \`echo\` annotates; \`run\` executes a referenced fragment inline.
|
|
@@ -150688,6 +150982,10 @@ function stepTarget(step) {
|
|
|
150688
150982
|
const scale = `scale ${step.scale}`;
|
|
150689
150983
|
return step.selector ? `${selectorLabel2(step.selector)} (${scale})` : scale;
|
|
150690
150984
|
}
|
|
150985
|
+
case "rotate": {
|
|
150986
|
+
const by = `by ${step.by}\xB0`;
|
|
150987
|
+
return step.selector ? `${selectorLabel2(step.selector)} (${by})` : by;
|
|
150988
|
+
}
|
|
150691
150989
|
case "snapshot":
|
|
150692
150990
|
return `"${step.name}"`;
|
|
150693
150991
|
default:
|
|
@@ -150870,7 +151168,8 @@ async function execLeafStep(state3, step, index, scope) {
|
|
|
150870
151168
|
case "await":
|
|
150871
151169
|
case "assert":
|
|
150872
151170
|
case "scroll-to":
|
|
150873
|
-
case "pinch":
|
|
151171
|
+
case "pinch":
|
|
151172
|
+
case "rotate": {
|
|
150874
151173
|
try {
|
|
150875
151174
|
const r = await runDirective(state3, step);
|
|
150876
151175
|
if (r.aborted) return { ...base, status: "skip", reason: r.reason };
|
|
@@ -150994,7 +151293,7 @@ init_zod();
|
|
|
150994
151293
|
|
|
150995
151294
|
// ../tool-server/src/utils/workspace-reader.ts
|
|
150996
151295
|
var import_promises12 = require("node:fs/promises");
|
|
150997
|
-
var
|
|
151296
|
+
var import_node_path21 = require("node:path");
|
|
150998
151297
|
var import_node_child_process27 = require("node:child_process");
|
|
150999
151298
|
var import_dotenv = __toESM(require_main());
|
|
151000
151299
|
async function exists(path38) {
|
|
@@ -151082,7 +151381,7 @@ async function detectLockfile(workspacePath) {
|
|
|
151082
151381
|
const checks = await Promise.all(
|
|
151083
151382
|
LOCKFILES.map(async (name) => ({
|
|
151084
151383
|
name,
|
|
151085
|
-
exists: await exists((0,
|
|
151384
|
+
exists: await exists((0, import_node_path21.join)(workspacePath, name))
|
|
151086
151385
|
}))
|
|
151087
151386
|
);
|
|
151088
151387
|
return checks.find((c) => c.exists)?.name ?? null;
|
|
@@ -151101,7 +151400,7 @@ var CI_CONFIGS = [
|
|
|
151101
151400
|
];
|
|
151102
151401
|
async function detectCiConfig(workspacePath) {
|
|
151103
151402
|
for (const ci of CI_CONFIGS) {
|
|
151104
|
-
if (await exists((0,
|
|
151403
|
+
if (await exists((0, import_node_path21.join)(workspacePath, ci.path))) return ci.label;
|
|
151105
151404
|
}
|
|
151106
151405
|
return null;
|
|
151107
151406
|
}
|
|
@@ -151122,7 +151421,7 @@ async function detectLintStagedConfig(workspacePath, packageJson) {
|
|
|
151122
151421
|
return packageJson["lint-staged"];
|
|
151123
151422
|
}
|
|
151124
151423
|
for (const file2 of LINT_STAGED_FILES) {
|
|
151125
|
-
const full = (0,
|
|
151424
|
+
const full = (0, import_node_path21.join)(workspacePath, file2);
|
|
151126
151425
|
if (await exists(full)) {
|
|
151127
151426
|
if (file2.endsWith(".json") || file2 === ".lintstagedrc") {
|
|
151128
151427
|
return readJsonFile(full);
|
|
@@ -151164,7 +151463,7 @@ var CONFIG_FILES = [
|
|
|
151164
151463
|
async function detectConfigFiles(workspacePath) {
|
|
151165
151464
|
const results = await Promise.allSettled(
|
|
151166
151465
|
CONFIG_FILES.map(async (file2) => {
|
|
151167
|
-
if (await exists((0,
|
|
151466
|
+
if (await exists((0, import_node_path21.join)(workspacePath, file2))) return file2;
|
|
151168
151467
|
return null;
|
|
151169
151468
|
})
|
|
151170
151469
|
);
|
|
@@ -151203,7 +151502,7 @@ async function detectEnvFiles(workspacePath) {
|
|
|
151203
151502
|
const envFileNames = entries.filter((e) => e === ".env" || e.startsWith(".env."));
|
|
151204
151503
|
const results = await Promise.all(
|
|
151205
151504
|
envFileNames.map(async (name) => {
|
|
151206
|
-
const content = await readTextFile((0,
|
|
151505
|
+
const content = await readTextFile((0, import_node_path21.join)(workspacePath, name));
|
|
151207
151506
|
return {
|
|
151208
151507
|
name,
|
|
151209
151508
|
keys: content ? extractEnvKeys(content) : []
|
|
@@ -151230,34 +151529,34 @@ async function readWorkspaceSnapshot(workspacePath) {
|
|
|
151230
151529
|
ciConfig,
|
|
151231
151530
|
configFilesFound
|
|
151232
151531
|
] = await Promise.all([
|
|
151233
|
-
readJsonFile((0,
|
|
151234
|
-
readJsonFile((0,
|
|
151235
|
-
readJsonFile((0,
|
|
151236
|
-
readJsonFile((0,
|
|
151237
|
-
readTextFile((0,
|
|
151238
|
-
(r) => r ?? readTextFile((0,
|
|
151532
|
+
readJsonFile((0, import_node_path21.join)(workspacePath, "package.json")),
|
|
151533
|
+
readJsonFile((0, import_node_path21.join)(workspacePath, "app.json")),
|
|
151534
|
+
readJsonFile((0, import_node_path21.join)(workspacePath, "eas.json")),
|
|
151535
|
+
readJsonFile((0, import_node_path21.join)(workspacePath, "tsconfig.json")),
|
|
151536
|
+
readTextFile((0, import_node_path21.join)(workspacePath, "metro.config.js")).then(
|
|
151537
|
+
(r) => r ?? readTextFile((0, import_node_path21.join)(workspacePath, "metro.config.ts"))
|
|
151239
151538
|
),
|
|
151240
|
-
readTextFile((0,
|
|
151241
|
-
(r) => r ?? readTextFile((0,
|
|
151539
|
+
readTextFile((0, import_node_path21.join)(workspacePath, "babel.config.js")).then(
|
|
151540
|
+
(r) => r ?? readTextFile((0, import_node_path21.join)(workspacePath, "babel.config.cjs"))
|
|
151242
151541
|
),
|
|
151243
|
-
isDirectory((0,
|
|
151244
|
-
isDirectory((0,
|
|
151542
|
+
isDirectory((0, import_node_path21.join)(workspacePath, "ios")),
|
|
151543
|
+
isDirectory((0, import_node_path21.join)(workspacePath, "android")),
|
|
151245
151544
|
detectLockfile(workspacePath),
|
|
151246
151545
|
detectEnvFiles(workspacePath),
|
|
151247
151546
|
detectToolVersions(workspacePath),
|
|
151248
|
-
listDir((0,
|
|
151249
|
-
listDir((0,
|
|
151547
|
+
listDir((0, import_node_path21.join)(workspacePath, "scripts")),
|
|
151548
|
+
listDir((0, import_node_path21.join)(workspacePath, ".husky")),
|
|
151250
151549
|
detectCiConfig(workspacePath),
|
|
151251
151550
|
detectConfigFiles(workspacePath)
|
|
151252
151551
|
]);
|
|
151253
|
-
const iosDir = (0,
|
|
151254
|
-
const androidDir = (0,
|
|
151552
|
+
const iosDir = (0, import_node_path21.join)(workspacePath, "ios");
|
|
151553
|
+
const androidDir = (0, import_node_path21.join)(workspacePath, "android");
|
|
151255
151554
|
const [iosWorkspace, iosHasPodfile, makefileText, lintStagedConfig, androidHasGradle] = await Promise.all([
|
|
151256
151555
|
hasIosDir ? findIosWorkspace(iosDir) : Promise.resolve(null),
|
|
151257
|
-
exists((0,
|
|
151258
|
-
readTextFile((0,
|
|
151556
|
+
exists((0, import_node_path21.join)(workspacePath, "ios", "Podfile")),
|
|
151557
|
+
readTextFile((0, import_node_path21.join)(workspacePath, "Makefile")),
|
|
151259
151558
|
detectLintStagedConfig(workspacePath, packageJson),
|
|
151260
|
-
hasAndroidDir ? exists((0,
|
|
151559
|
+
hasAndroidDir ? exists((0, import_node_path21.join)(androidDir, "gradlew")) : Promise.resolve(false)
|
|
151261
151560
|
]);
|
|
151262
151561
|
const metroPort = metroConfigRaw ? extractMetroPort(metroConfigRaw) : null;
|
|
151263
151562
|
const makefileTargets = makefileText ? extractMakefileTargets(makefileText) : null;
|