codeloop-mcp-server 0.1.88 → 0.1.91
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/evidence/interaction_coverage.d.ts.map +1 -1
- package/dist/evidence/interaction_coverage.js +2 -0
- package/dist/evidence/interaction_coverage.js.map +1 -1
- package/dist/index.js +236 -54
- package/dist/index.js.map +1 -1
- package/dist/runners/active_target.d.ts +31 -0
- package/dist/runners/active_target.d.ts.map +1 -0
- package/dist/runners/active_target.js +65 -0
- package/dist/runners/active_target.js.map +1 -0
- package/dist/runners/app_launcher.d.ts +37 -1
- package/dist/runners/app_launcher.d.ts.map +1 -1
- package/dist/runners/app_launcher.js +114 -15
- package/dist/runners/app_launcher.js.map +1 -1
- package/dist/runners/app_logger.d.ts +1 -1
- package/dist/runners/app_logger.d.ts.map +1 -1
- package/dist/runners/app_logger.js +9 -8
- package/dist/runners/app_logger.js.map +1 -1
- package/dist/runners/device_probe.d.ts +18 -0
- package/dist/runners/device_probe.d.ts.map +1 -1
- package/dist/runners/device_probe.js +47 -20
- package/dist/runners/device_probe.js.map +1 -1
- package/dist/runners/interaction_engine.d.ts +6 -0
- package/dist/runners/interaction_engine.d.ts.map +1 -1
- package/dist/runners/interaction_engine.js +64 -21
- package/dist/runners/interaction_engine.js.map +1 -1
- package/dist/runners/ios_sim_input.d.ts +106 -0
- package/dist/runners/ios_sim_input.d.ts.map +1 -0
- package/dist/runners/ios_sim_input.js +453 -0
- package/dist/runners/ios_sim_input.js.map +1 -0
- package/dist/runners/maestro.d.ts +27 -2
- package/dist/runners/maestro.d.ts.map +1 -1
- package/dist/runners/maestro.js +57 -7
- package/dist/runners/maestro.js.map +1 -1
- package/dist/runners/maestro_generator.d.ts +1 -1
- package/dist/runners/maestro_generator.d.ts.map +1 -1
- package/dist/runners/maestro_generator.js +3 -2
- package/dist/runners/maestro_generator.js.map +1 -1
- package/dist/runners/screenshot.d.ts +6 -0
- package/dist/runners/screenshot.d.ts.map +1 -1
- package/dist/runners/screenshot.js +10 -9
- package/dist/runners/screenshot.js.map +1 -1
- package/dist/runners/video_recorder.d.ts +3 -1
- package/dist/runners/video_recorder.d.ts.map +1 -1
- package/dist/runners/video_recorder.js +19 -6
- package/dist/runners/video_recorder.js.map +1 -1
- package/dist/runners/window_manager.d.ts +55 -25
- package/dist/runners/window_manager.d.ts.map +1 -1
- package/dist/runners/window_manager.js +171 -71
- package/dist/runners/window_manager.js.map +1 -1
- package/dist/tools/discover_interactions.d.ts +22 -0
- package/dist/tools/discover_interactions.d.ts.map +1 -1
- package/dist/tools/discover_interactions.js +278 -5
- package/dist/tools/discover_interactions.js.map +1 -1
- package/dist/tools/discover_screens.d.ts +2 -0
- package/dist/tools/discover_screens.d.ts.map +1 -1
- package/dist/tools/discover_screens.js +139 -1
- package/dist/tools/discover_screens.js.map +1 -1
- package/dist/tools/run_journey.d.ts +6 -0
- package/dist/tools/run_journey.d.ts.map +1 -1
- package/dist/tools/run_journey.js +75 -16
- package/dist/tools/run_journey.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* iOS Simulator input backend — REAL tap/type/swipe/key for the Simulator.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists: `xcrun simctl io` only supports `enumerate / poll /
|
|
5
|
+
* recordVideo / screenshot`. The old `simctl io booted tap/type/swipe/
|
|
6
|
+
* sendkey` helpers in window_manager.ts invoked subcommands that have
|
|
7
|
+
* NEVER existed in any Xcode release, so every coordinate-driven iOS
|
|
8
|
+
* interaction failed unconditionally (E2E 11, WedCheese run).
|
|
9
|
+
*
|
|
10
|
+
* Strategy (in preference order):
|
|
11
|
+
* 1. `idb` (Facebook's iOS debug bridge) when on PATH — drives HID
|
|
12
|
+
* events directly into the simulator (`idb ui tap/swipe/text`),
|
|
13
|
+
* no window focus needed, coordinates are device POINTS.
|
|
14
|
+
* 2. CGEvent via the Simulator window (always available on macOS):
|
|
15
|
+
* bring Simulator frontmost → resolve the DEVICE CONTENT rect →
|
|
16
|
+
* a fresh device screenshot gives a self-calibrating map from
|
|
17
|
+
* screenshot pixels → host screen points → post real events.
|
|
18
|
+
* The content rect comes from the Accessibility tree (the device
|
|
19
|
+
* screen is an AXGroup sized exactly device-points — robust to
|
|
20
|
+
* bezels, zoom levels, and toolbar chrome); when AX is unavailable
|
|
21
|
+
* we fall back to window-bounds width-ratio calibration (default
|
|
22
|
+
* window mode only).
|
|
23
|
+
*
|
|
24
|
+
* Coordinate convention: callers pass coordinates in DEVICE PIXELS as
|
|
25
|
+
* seen on a `simctl io screenshot` PNG (that's what the agent's vision
|
|
26
|
+
* works from). Both backends translate internally.
|
|
27
|
+
*/
|
|
28
|
+
import { join } from "path";
|
|
29
|
+
import { tmpdir, platform } from "os";
|
|
30
|
+
import { unlinkSync, existsSync } from "fs";
|
|
31
|
+
import { runCommand, checkToolAvailable } from "./base.js";
|
|
32
|
+
import { readPngDims } from "./png_dims.js";
|
|
33
|
+
import { getWindowBounds, bringAppToFront, clickAtPosition, dragDrop, longPressAtPosition, typeText, sendKeyByName, sendHotkey, } from "./window_manager.js";
|
|
34
|
+
const SIMULATOR_APP = "Simulator";
|
|
35
|
+
/** Calibration is re-used briefly so a tap+type sequence doesn't re-screenshot. */
|
|
36
|
+
const MAPPING_TTL_MS = 5_000;
|
|
37
|
+
/** Window chrome (title bar) sanity range, in screen points. */
|
|
38
|
+
const MAX_CHROME_PT = 120;
|
|
39
|
+
const SCREENSHOT_TIMEOUT_MS = 20_000;
|
|
40
|
+
const IDB_TIMEOUT_MS = 30_000;
|
|
41
|
+
// ── idb backend detection ───────────────────────────────────────────
|
|
42
|
+
let idbAvailableCache = null;
|
|
43
|
+
export async function isIdbAvailable() {
|
|
44
|
+
if (idbAvailableCache !== null)
|
|
45
|
+
return idbAvailableCache;
|
|
46
|
+
idbAvailableCache = await checkToolAvailable("idb");
|
|
47
|
+
return idbAvailableCache;
|
|
48
|
+
}
|
|
49
|
+
/** Test hook — reset memoized idb detection + calibration. */
|
|
50
|
+
export function resetIosInputCaches() {
|
|
51
|
+
idbAvailableCache = null;
|
|
52
|
+
mappingCache.clear();
|
|
53
|
+
}
|
|
54
|
+
const idbDimsCache = new Map();
|
|
55
|
+
/**
|
|
56
|
+
* idb expects coordinates in device POINTS; our callers supply device
|
|
57
|
+
* PIXELS (screenshot space). `idb describe` exposes both so we can
|
|
58
|
+
* scale. Cached per udid — device geometry never changes while booted.
|
|
59
|
+
*/
|
|
60
|
+
async function idbScreenDims(udid) {
|
|
61
|
+
const cached = idbDimsCache.get(udid);
|
|
62
|
+
if (cached)
|
|
63
|
+
return cached;
|
|
64
|
+
const args = ["describe", "--json"];
|
|
65
|
+
if (udid && udid !== "booted")
|
|
66
|
+
args.push("--udid", udid);
|
|
67
|
+
const result = await runCommand("idb", args, process.cwd(), undefined, undefined, IDB_TIMEOUT_MS);
|
|
68
|
+
if (result.exit_code !== 0)
|
|
69
|
+
return null;
|
|
70
|
+
try {
|
|
71
|
+
const parsed = JSON.parse(result.stdout);
|
|
72
|
+
const sd = parsed.screen_dimensions;
|
|
73
|
+
if (!sd?.width || !sd.height || !sd.width_points || !sd.height_points)
|
|
74
|
+
return null;
|
|
75
|
+
const dims = {
|
|
76
|
+
widthPixels: sd.width,
|
|
77
|
+
heightPixels: sd.height,
|
|
78
|
+
widthPoints: sd.width_points,
|
|
79
|
+
heightPoints: sd.height_points,
|
|
80
|
+
};
|
|
81
|
+
idbDimsCache.set(udid, dims);
|
|
82
|
+
return dims;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function idbUdidArgs(udid) {
|
|
89
|
+
return udid && udid !== "booted" ? ["--udid", udid] : [];
|
|
90
|
+
}
|
|
91
|
+
/** Convert device-pixel coords → device points for idb. */
|
|
92
|
+
async function toIdbPoints(udid, x, y) {
|
|
93
|
+
const dims = await idbScreenDims(udid);
|
|
94
|
+
if (!dims)
|
|
95
|
+
return null;
|
|
96
|
+
const factor = dims.widthPixels > 0 ? dims.widthPoints / dims.widthPixels : 1;
|
|
97
|
+
return { x: Math.round(x * factor), y: Math.round(y * factor) };
|
|
98
|
+
}
|
|
99
|
+
// ── CGEvent backend: content-rect calibration ───────────────────────
|
|
100
|
+
const mappingCache = new Map();
|
|
101
|
+
/**
|
|
102
|
+
* The device screen inside the Simulator window is an AXGroup whose AX
|
|
103
|
+
* frame is exactly the content rect in global screen points — robust to
|
|
104
|
+
* device bezels, zoom level, and toolbar chrome (all of which break the
|
|
105
|
+
* naive "content spans the window width" assumption; live debugging
|
|
106
|
+
* showed Xcode 26's Simulator adds bezel margins by default).
|
|
107
|
+
*
|
|
108
|
+
* Returns every device-content group across Simulator windows so the
|
|
109
|
+
* caller can match the window title to the pinned device's name.
|
|
110
|
+
*/
|
|
111
|
+
async function getSimulatorContentRects() {
|
|
112
|
+
const script = [
|
|
113
|
+
'set out to ""',
|
|
114
|
+
'tell application "System Events" to tell process "Simulator"',
|
|
115
|
+
' repeat with w in windows',
|
|
116
|
+
' try',
|
|
117
|
+
' repeat with el in UI elements of w',
|
|
118
|
+
' if role of el is "AXGroup" then',
|
|
119
|
+
' set p to position of el',
|
|
120
|
+
' set s to size of el',
|
|
121
|
+
' set out to out & (name of w) & "|" & (item 1 of p) & "|" & (item 2 of p) & "|" & (item 1 of s) & "|" & (item 2 of s) & linefeed',
|
|
122
|
+
' end if',
|
|
123
|
+
' end repeat',
|
|
124
|
+
' end try',
|
|
125
|
+
' end repeat',
|
|
126
|
+
'end tell',
|
|
127
|
+
'return out',
|
|
128
|
+
].join("\n");
|
|
129
|
+
const r = await runCommand("osascript", ["-e", script], process.cwd(), undefined, undefined, 15_000);
|
|
130
|
+
if (r.exit_code !== 0)
|
|
131
|
+
return [];
|
|
132
|
+
const rects = [];
|
|
133
|
+
for (const line of r.stdout.split("\n")) {
|
|
134
|
+
const parts = line.trim().split("|");
|
|
135
|
+
if (parts.length !== 5)
|
|
136
|
+
continue;
|
|
137
|
+
const [windowName, xs, ys, ws, hs] = parts;
|
|
138
|
+
const x = Number(xs), y = Number(ys), width = Number(ws), height = Number(hs);
|
|
139
|
+
if ([x, y, width, height].some((n) => !Number.isFinite(n)) || width < 50 || height < 50)
|
|
140
|
+
continue;
|
|
141
|
+
rects.push({ x, y, width, height, windowName });
|
|
142
|
+
}
|
|
143
|
+
return rects;
|
|
144
|
+
}
|
|
145
|
+
/** Booted-device name for a udid (so multi-sim setups pick the right window). */
|
|
146
|
+
async function simDeviceName(udid) {
|
|
147
|
+
if (!udid || udid === "booted")
|
|
148
|
+
return null;
|
|
149
|
+
const r = await runCommand("xcrun", ["simctl", "list", "devices", "booted"], process.cwd(), undefined, undefined, 15_000);
|
|
150
|
+
if (r.exit_code !== 0)
|
|
151
|
+
return null;
|
|
152
|
+
const m = r.stdout.split("\n").find((l) => l.includes(udid));
|
|
153
|
+
if (!m)
|
|
154
|
+
return null;
|
|
155
|
+
const name = m.trim().split(`(${udid}`)[0]?.trim();
|
|
156
|
+
return name && name.length > 0 ? name : null;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Self-calibrating screenshot-pixel → host-screen-point mapping.
|
|
160
|
+
*
|
|
161
|
+
* 1. Bring Simulator frontmost (CGEvents land on whatever is under
|
|
162
|
+
* the cursor — we need our window there, and key events need focus).
|
|
163
|
+
* 2. Resolve the device CONTENT rect via the AX tree (preferred), or
|
|
164
|
+
* fall back to `getWindowBounds("Simulator")` width-ratio math
|
|
165
|
+
* (default window mode, no bezels, title bar only).
|
|
166
|
+
* 3. `simctl io <udid> screenshot` + IHDR read → device pixel dims →
|
|
167
|
+
* scale = contentWidth / pngWidth.
|
|
168
|
+
*
|
|
169
|
+
* Returns null when no Simulator window can be resolved or the
|
|
170
|
+
* geometry is implausible — callers surface a directive instead of
|
|
171
|
+
* clicking blind.
|
|
172
|
+
*/
|
|
173
|
+
export async function resolveSimMapping(udid = "booted", opts) {
|
|
174
|
+
// os.platform() (not process.platform) so unit tests can mock the host —
|
|
175
|
+
// CI runs this suite on Linux where the real value would null every mapping.
|
|
176
|
+
if (platform() !== "darwin")
|
|
177
|
+
return null;
|
|
178
|
+
const cacheKey = udid || "booted";
|
|
179
|
+
if (!opts?.forceRefresh) {
|
|
180
|
+
const cached = mappingCache.get(cacheKey);
|
|
181
|
+
if (cached && Date.now() - cached.capturedAt < MAPPING_TTL_MS)
|
|
182
|
+
return cached;
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
await bringAppToFront(SIMULATOR_APP);
|
|
186
|
+
}
|
|
187
|
+
catch { /* best-effort */ }
|
|
188
|
+
// Give the window server a beat to finish the front-switch animation.
|
|
189
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
190
|
+
const shotPath = join(tmpdir(), `codeloop_sim_cal_${Date.now()}.png`);
|
|
191
|
+
try {
|
|
192
|
+
const shot = await runCommand("xcrun", ["simctl", "io", cacheKey, "screenshot", shotPath], process.cwd(), undefined, undefined, SCREENSHOT_TIMEOUT_MS);
|
|
193
|
+
if (shot.exit_code !== 0 || !existsSync(shotPath))
|
|
194
|
+
return null;
|
|
195
|
+
const dims = readPngDims(shotPath);
|
|
196
|
+
if (!dims)
|
|
197
|
+
return null;
|
|
198
|
+
const aspect = dims.height / dims.width;
|
|
199
|
+
// Preferred: exact content rect from the AX tree.
|
|
200
|
+
const rects = await getSimulatorContentRects();
|
|
201
|
+
if (rects.length > 0) {
|
|
202
|
+
const wantedName = await simDeviceName(cacheKey);
|
|
203
|
+
const matching = wantedName ? rects.filter((r) => r.windowName.includes(wantedName)) : rects;
|
|
204
|
+
// Among candidates prefer the one whose aspect matches this device's
|
|
205
|
+
// screenshot (belt + suspenders when several sims are visible).
|
|
206
|
+
const pool = matching.length > 0 ? matching : rects;
|
|
207
|
+
const best = pool
|
|
208
|
+
.map((r) => ({ r, aspectErr: Math.abs(r.height / r.width - aspect) / aspect }))
|
|
209
|
+
.sort((a, b) => a.aspectErr - b.aspectErr)[0];
|
|
210
|
+
if (best && best.aspectErr < 0.05) {
|
|
211
|
+
const mapping = {
|
|
212
|
+
bounds: { x: best.r.x, y: best.r.y, width: best.r.width, height: best.r.height },
|
|
213
|
+
pngWidth: dims.width,
|
|
214
|
+
pngHeight: dims.height,
|
|
215
|
+
scale: best.r.width / dims.width,
|
|
216
|
+
chromeTop: 0,
|
|
217
|
+
method: "ax",
|
|
218
|
+
udid: cacheKey,
|
|
219
|
+
capturedAt: Date.now(),
|
|
220
|
+
};
|
|
221
|
+
mappingCache.set(cacheKey, mapping);
|
|
222
|
+
return mapping;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// Fallback: window bounds + width-ratio (default window mode only —
|
|
226
|
+
// content spans the full width with a title bar on top).
|
|
227
|
+
const bounds = await getWindowBounds(SIMULATOR_APP);
|
|
228
|
+
if (!bounds || bounds.width <= 0 || bounds.height <= 0)
|
|
229
|
+
return null;
|
|
230
|
+
const scale = bounds.width / dims.width;
|
|
231
|
+
const chromeTop = bounds.height - dims.height * scale;
|
|
232
|
+
if (!Number.isFinite(scale) || scale <= 0 || chromeTop < -2 || chromeTop > MAX_CHROME_PT) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
const mapping = {
|
|
236
|
+
bounds,
|
|
237
|
+
pngWidth: dims.width,
|
|
238
|
+
pngHeight: dims.height,
|
|
239
|
+
scale,
|
|
240
|
+
chromeTop: Math.max(0, chromeTop),
|
|
241
|
+
method: "window-ratio",
|
|
242
|
+
udid: cacheKey,
|
|
243
|
+
capturedAt: Date.now(),
|
|
244
|
+
};
|
|
245
|
+
mappingCache.set(cacheKey, mapping);
|
|
246
|
+
return mapping;
|
|
247
|
+
}
|
|
248
|
+
finally {
|
|
249
|
+
try {
|
|
250
|
+
unlinkSync(shotPath);
|
|
251
|
+
}
|
|
252
|
+
catch { /* ignore */ }
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/** Map a device-pixel coordinate to a host-screen point. Exported for tests. */
|
|
256
|
+
export function mapToScreenPoint(mapping, x, y) {
|
|
257
|
+
return {
|
|
258
|
+
x: mapping.bounds.x + x * mapping.scale,
|
|
259
|
+
y: mapping.bounds.y + mapping.chromeTop + y * mapping.scale,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
const MAPPING_FAIL_DETAIL = "could not calibrate the Simulator window (is the Simulator app open and visible, with Accessibility + Screen Recording permissions granted to the agent's terminal?). " +
|
|
263
|
+
"Try: open -a Simulator, ensure the device window is on screen, then retry. " +
|
|
264
|
+
"Installing idb (`brew install idb-companion && pipx install fb-idb`) enables window-independent input.";
|
|
265
|
+
// ── Public input actions ─────────────────────────────────────────────
|
|
266
|
+
export async function iosSimTap(x, y, udid = "booted") {
|
|
267
|
+
if (await isIdbAvailable()) {
|
|
268
|
+
const pt = await toIdbPoints(udid, x, y);
|
|
269
|
+
if (pt) {
|
|
270
|
+
const r = await runCommand("idb", ["ui", "tap", ...idbUdidArgs(udid), String(pt.x), String(pt.y)], process.cwd(), undefined, undefined, IDB_TIMEOUT_MS);
|
|
271
|
+
if (r.exit_code === 0) {
|
|
272
|
+
return { success: true, backend: "idb", detail: `idb tap (${pt.x},${pt.y}) pt` };
|
|
273
|
+
}
|
|
274
|
+
// fall through to CGEvent on idb failure
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
const mapping = await resolveSimMapping(udid);
|
|
278
|
+
if (!mapping)
|
|
279
|
+
return { success: false, backend: "none", detail: MAPPING_FAIL_DETAIL };
|
|
280
|
+
const pt = mapToScreenPoint(mapping, x, y);
|
|
281
|
+
const ok = await clickAtPosition(pt.x, pt.y);
|
|
282
|
+
return {
|
|
283
|
+
success: ok,
|
|
284
|
+
backend: "cgevent",
|
|
285
|
+
detail: ok
|
|
286
|
+
? `cgevent tap px(${Math.round(x)},${Math.round(y)}) → screen(${Math.round(pt.x)},${Math.round(pt.y)})`
|
|
287
|
+
: "CGEvent click failed (Accessibility permission?)",
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
export async function iosSimLongPress(x, y, durationMs = 1000, udid = "booted") {
|
|
291
|
+
// idb has no long-press primitive; CGEvent press-and-hold works on the window.
|
|
292
|
+
const mapping = await resolveSimMapping(udid);
|
|
293
|
+
if (!mapping)
|
|
294
|
+
return { success: false, backend: "none", detail: MAPPING_FAIL_DETAIL };
|
|
295
|
+
const pt = mapToScreenPoint(mapping, x, y);
|
|
296
|
+
const ok = await longPressAtPosition(pt.x, pt.y, durationMs);
|
|
297
|
+
return {
|
|
298
|
+
success: ok,
|
|
299
|
+
backend: "cgevent",
|
|
300
|
+
detail: ok ? `cgevent long-press ${durationMs}ms at screen(${Math.round(pt.x)},${Math.round(pt.y)})` : "CGEvent long-press failed",
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
export async function iosSimSwipe(x1, y1, x2, y2, durationMs = 300, udid = "booted") {
|
|
304
|
+
if (await isIdbAvailable()) {
|
|
305
|
+
const a = await toIdbPoints(udid, x1, y1);
|
|
306
|
+
const b = await toIdbPoints(udid, x2, y2);
|
|
307
|
+
if (a && b) {
|
|
308
|
+
const r = await runCommand("idb", [
|
|
309
|
+
"ui", "swipe", ...idbUdidArgs(udid),
|
|
310
|
+
"--duration", String(Math.max(0.05, durationMs / 1000)),
|
|
311
|
+
String(a.x), String(a.y), String(b.x), String(b.y),
|
|
312
|
+
], process.cwd(), undefined, undefined, IDB_TIMEOUT_MS);
|
|
313
|
+
if (r.exit_code === 0) {
|
|
314
|
+
return { success: true, backend: "idb", detail: `idb swipe (${a.x},${a.y})→(${b.x},${b.y}) pt` };
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const mapping = await resolveSimMapping(udid);
|
|
319
|
+
if (!mapping)
|
|
320
|
+
return { success: false, backend: "none", detail: MAPPING_FAIL_DETAIL };
|
|
321
|
+
const a = mapToScreenPoint(mapping, x1, y1);
|
|
322
|
+
const b = mapToScreenPoint(mapping, x2, y2);
|
|
323
|
+
// CGEvent drag on the simulator content == touch swipe on the device.
|
|
324
|
+
const ok = await dragDrop(a.x, a.y, b.x, b.y, durationMs);
|
|
325
|
+
return {
|
|
326
|
+
success: ok,
|
|
327
|
+
backend: "cgevent",
|
|
328
|
+
detail: ok
|
|
329
|
+
? `cgevent swipe screen(${Math.round(a.x)},${Math.round(a.y)})→(${Math.round(b.x)},${Math.round(b.y)})`
|
|
330
|
+
: "CGEvent drag failed (Accessibility permission?)",
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
export async function iosSimType(text, udid = "booted") {
|
|
334
|
+
if (text.length === 0)
|
|
335
|
+
return { success: true, backend: "none", detail: "empty text" };
|
|
336
|
+
if (await isIdbAvailable()) {
|
|
337
|
+
const r = await runCommand("idb", ["ui", "text", ...idbUdidArgs(udid), text], process.cwd(), undefined, undefined, IDB_TIMEOUT_MS);
|
|
338
|
+
if (r.exit_code === 0) {
|
|
339
|
+
return { success: true, backend: "idb", detail: `idb text (${text.length} chars)` };
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
// osascript keystroke goes to the frontmost app; Simulator forwards
|
|
343
|
+
// host keyboard input to the device when "Send Keyboard Input to
|
|
344
|
+
// Device" is enabled (the default).
|
|
345
|
+
try {
|
|
346
|
+
await bringAppToFront(SIMULATOR_APP);
|
|
347
|
+
}
|
|
348
|
+
catch { /* best-effort */ }
|
|
349
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
350
|
+
const ok = await typeText(text);
|
|
351
|
+
return {
|
|
352
|
+
success: ok,
|
|
353
|
+
backend: "cgevent",
|
|
354
|
+
detail: ok ? `osascript keystroke (${text.length} chars)` : "osascript keystroke failed (Accessibility permission?)",
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Send a named special key (return/tab/escape/arrows/…) to the simulator.
|
|
359
|
+
* Uses the same key-name normalisation as the desktop path.
|
|
360
|
+
*/
|
|
361
|
+
export async function iosSimKey(keyName, udid = "booted") {
|
|
362
|
+
if (await isIdbAvailable()) {
|
|
363
|
+
// idb key takes an Apple HID keycode; map the common ones.
|
|
364
|
+
const HID = {
|
|
365
|
+
return: 40, enter: 40, tab: 43, space: 44, escape: 41, esc: 41,
|
|
366
|
+
backspace: 42, delete: 42, "forward-delete": 76,
|
|
367
|
+
up: 82, "up-arrow": 82, down: 81, "down-arrow": 81,
|
|
368
|
+
left: 80, "left-arrow": 80, right: 79, "right-arrow": 79,
|
|
369
|
+
home: 74, end: 77,
|
|
370
|
+
};
|
|
371
|
+
const code = HID[keyName.toLowerCase()];
|
|
372
|
+
if (code != null) {
|
|
373
|
+
const r = await runCommand("idb", ["ui", "key", ...idbUdidArgs(udid), String(code)], process.cwd(), undefined, undefined, IDB_TIMEOUT_MS);
|
|
374
|
+
if (r.exit_code === 0)
|
|
375
|
+
return { success: true, backend: "idb", detail: `idb key ${keyName}` };
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
try {
|
|
379
|
+
await bringAppToFront(SIMULATOR_APP);
|
|
380
|
+
}
|
|
381
|
+
catch { /* best-effort */ }
|
|
382
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
383
|
+
const ok = await sendKeyByName(keyName);
|
|
384
|
+
return {
|
|
385
|
+
success: ok,
|
|
386
|
+
backend: "cgevent",
|
|
387
|
+
detail: ok ? `cgevent key ${keyName}` : `key "${keyName}" failed or unknown`,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Rotate the simulator via the Device menu shortcuts (Cmd+Left / Cmd+Right).
|
|
392
|
+
* There is no simctl rotation command, so this drives the Simulator app —
|
|
393
|
+
* P1.2 (0.1.91); previously rotate_device silently no-opped on iOS.
|
|
394
|
+
*
|
|
395
|
+
* `orientation` is a TARGET hint: we can't read the current orientation,
|
|
396
|
+
* so "landscape" rotates right once and "portrait" rotates left once —
|
|
397
|
+
* callers should verify with a screenshot (mapping cache is invalidated).
|
|
398
|
+
*/
|
|
399
|
+
export async function iosSimRotate(orientation, udid = "booted") {
|
|
400
|
+
try {
|
|
401
|
+
await bringAppToFront(SIMULATOR_APP);
|
|
402
|
+
}
|
|
403
|
+
catch { /* best-effort */ }
|
|
404
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
405
|
+
const combo = orientation === "landscape" ? "cmd+right" : "cmd+left";
|
|
406
|
+
const ok = await sendHotkey(combo);
|
|
407
|
+
// Window geometry changed — force recalibration on the next tap.
|
|
408
|
+
mappingCache.delete(udid || "booted");
|
|
409
|
+
return {
|
|
410
|
+
success: ok,
|
|
411
|
+
backend: "cgevent",
|
|
412
|
+
detail: ok
|
|
413
|
+
? `${combo} sent to Simulator (target ${orientation}); rotation has no state read-back — verify with a screenshot`
|
|
414
|
+
: `${combo} failed — is the Simulator window frontmost and Accessibility permission granted?`,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Read the accessibility/UI tree off the simulator — only possible via
|
|
419
|
+
* idb (`describe-all`). Returns null when idb is missing so callers can
|
|
420
|
+
* keep the honest "use vision on a screenshot" directive.
|
|
421
|
+
*/
|
|
422
|
+
export async function iosSimGetText(udid = "booted") {
|
|
423
|
+
if (!(await isIdbAvailable()))
|
|
424
|
+
return null;
|
|
425
|
+
const r = await runCommand("idb", ["ui", "describe-all", ...idbUdidArgs(udid)], process.cwd(), undefined, undefined, IDB_TIMEOUT_MS);
|
|
426
|
+
if (r.exit_code !== 0 || !r.stdout.trim())
|
|
427
|
+
return null;
|
|
428
|
+
// describe-all emits one JSON object per element; surface the visible
|
|
429
|
+
// labels/values as plain text for the agent.
|
|
430
|
+
const texts = [];
|
|
431
|
+
try {
|
|
432
|
+
const parsed = JSON.parse(r.stdout);
|
|
433
|
+
for (const el of parsed) {
|
|
434
|
+
const t = (el.AXLabel || el.AXValue || "").trim();
|
|
435
|
+
if (t && t !== "(null)")
|
|
436
|
+
texts.push(t);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
catch {
|
|
440
|
+
// Some idb versions emit NDJSON.
|
|
441
|
+
for (const line of r.stdout.split("\n")) {
|
|
442
|
+
try {
|
|
443
|
+
const el = JSON.parse(line);
|
|
444
|
+
const t = (el.AXLabel || el.AXValue || "").trim();
|
|
445
|
+
if (t && t !== "(null)")
|
|
446
|
+
texts.push(t);
|
|
447
|
+
}
|
|
448
|
+
catch { /* skip non-JSON lines */ }
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return texts.length > 0 ? texts.join("\n") : null;
|
|
452
|
+
}
|
|
453
|
+
//# sourceMappingURL=ios_sim_input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ios_sim_input.js","sourceRoot":"","sources":["../../src/runners/ios_sim_input.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,8 +8,33 @@ export interface MaestroResult {
|
|
|
8
8
|
logPath: string;
|
|
9
9
|
}
|
|
10
10
|
export declare function isMaestroInstalled(): Promise<boolean>;
|
|
11
|
+
/**
|
|
12
|
+
* Per-OS Maestro install directive. The old curl-only line dead-ended
|
|
13
|
+
* Windows hosts (no bash); Windows gets the PowerShell installer.
|
|
14
|
+
*/
|
|
15
|
+
export declare function maestroInstallDirective(): string;
|
|
16
|
+
/** Per-OS Java install directive (Maestro is a JVM CLI; no JVM = no Maestro). */
|
|
17
|
+
export declare function javaInstallDirective(): string;
|
|
18
|
+
export interface MaestroPreflight {
|
|
19
|
+
ok: boolean;
|
|
20
|
+
/** Actionable per-OS directive when not ok. */
|
|
21
|
+
directive?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Preflight the Maestro toolchain BEFORE driving: the `maestro` launcher is a
|
|
25
|
+
* shell script around a JVM CLI, so a missing/broken Java produces a cryptic
|
|
26
|
+
* mid-flow failure unless caught here. `java -version` exits 0 and prints the
|
|
27
|
+
* version to stderr on every JDK.
|
|
28
|
+
*/
|
|
29
|
+
export declare function maestroPreflight(): Promise<MaestroPreflight>;
|
|
30
|
+
/**
|
|
31
|
+
* Env for Maestro runs: the FIRST run on an iOS simulator installs +
|
|
32
|
+
* boots the XCTest driver, which routinely exceeds Maestro's default
|
|
33
|
+
* 15s driver-startup timeout — raise it so first runs don't flake.
|
|
34
|
+
*/
|
|
35
|
+
export declare function maestroRunEnv(): Record<string, string>;
|
|
11
36
|
export declare function findMaestroFlows(cwd: string): string[];
|
|
12
|
-
export declare function runMaestroTests(cwd: string, logDir: string): Promise<MaestroResult>;
|
|
37
|
+
export declare function runMaestroTests(cwd: string, logDir: string, device?: string): Promise<MaestroResult>;
|
|
13
38
|
/**
|
|
14
39
|
* 0.1.51 H9 — Adapt `runMaestroTests` to the `RunnerResult` shape used
|
|
15
40
|
* by every other verify runner so it can be pushed into the `results`
|
|
@@ -21,5 +46,5 @@ export declare function runMaestroTests(cwd: string, logDir: string): Promise<Ma
|
|
|
21
46
|
* `passed` flows count toward `passed`, `failed` flows toward `failed`,
|
|
22
47
|
* and `skipped` is always 0 (Maestro doesn't surface a skipped count).
|
|
23
48
|
*/
|
|
24
|
-
export declare function runMaestroFlows(cwd: string, logPath: string): Promise<RunnerResult>;
|
|
49
|
+
export declare function runMaestroFlows(cwd: string, logPath: string, device?: string): Promise<RunnerResult>;
|
|
25
50
|
//# sourceMappingURL=maestro.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"maestro.d.ts","sourceRoot":"","sources":["../../src/runners/maestro.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"maestro.d.ts","sourceRoot":"","sources":["../../src/runners/maestro.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAM9C,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;CACjB;AAID,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC,CAG3D;AAID;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAWhD;AAED,iFAAiF;AACjF,wBAAgB,oBAAoB,IAAI,MAAM,CAK7C;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,gBAAgB,CAAC,CASlE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEtD;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAQtD;AAuGD,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAqC1G;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,CA8BvB"}
|
package/dist/runners/maestro.js
CHANGED
|
@@ -2,11 +2,58 @@ import { runCommand, makeSkippedResult } from "./base.js";
|
|
|
2
2
|
import { MAESTRO_TIMEOUT_MS } from "./test_watchdog.js";
|
|
3
3
|
import { existsSync, mkdirSync, readdirSync } from "fs";
|
|
4
4
|
import { join } from "path";
|
|
5
|
+
import { platform } from "os";
|
|
5
6
|
const MAESTRO_FLOW_REL_DIRS = ["tests/maestro", ".maestro", "maestro"];
|
|
6
7
|
export async function isMaestroInstalled() {
|
|
7
8
|
const result = await runCommand("maestro", ["--version"], process.cwd());
|
|
8
9
|
return result.exit_code === 0;
|
|
9
10
|
}
|
|
11
|
+
// ── P0.4 (0.1.90): Maestro as a DEPENDABLE primary engine ───────────
|
|
12
|
+
/**
|
|
13
|
+
* Per-OS Maestro install directive. The old curl-only line dead-ended
|
|
14
|
+
* Windows hosts (no bash); Windows gets the PowerShell installer.
|
|
15
|
+
*/
|
|
16
|
+
export function maestroInstallDirective() {
|
|
17
|
+
if (platform() === "win32") {
|
|
18
|
+
return ("Install Maestro (PowerShell): `iwr -UseBasicParsing https://get.maestro.mobile.dev/install.ps1 | iex` " +
|
|
19
|
+
"(or download from https://maestro.mobile.dev). Maestro is a JVM CLI — it also needs Java 17+ on PATH.");
|
|
20
|
+
}
|
|
21
|
+
return ("Install Maestro: `curl -Ls https://get.maestro.mobile.dev | bash`. " +
|
|
22
|
+
"Maestro is a JVM CLI — it also needs Java 17+ on PATH.");
|
|
23
|
+
}
|
|
24
|
+
/** Per-OS Java install directive (Maestro is a JVM CLI; no JVM = no Maestro). */
|
|
25
|
+
export function javaInstallDirective() {
|
|
26
|
+
const os = platform();
|
|
27
|
+
if (os === "darwin")
|
|
28
|
+
return "Install Java 17+ for Maestro: `brew install --cask temurin` (then re-open the terminal).";
|
|
29
|
+
if (os === "win32")
|
|
30
|
+
return "Install Java 17+ for Maestro: `winget install EclipseAdoptium.Temurin.21.JDK` (then re-open the terminal).";
|
|
31
|
+
return "Install Java 17+ for Maestro: `sudo apt install openjdk-21-jre` (or your distro's OpenJDK 17+ package).";
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Preflight the Maestro toolchain BEFORE driving: the `maestro` launcher is a
|
|
35
|
+
* shell script around a JVM CLI, so a missing/broken Java produces a cryptic
|
|
36
|
+
* mid-flow failure unless caught here. `java -version` exits 0 and prints the
|
|
37
|
+
* version to stderr on every JDK.
|
|
38
|
+
*/
|
|
39
|
+
export async function maestroPreflight() {
|
|
40
|
+
if (!(await isMaestroInstalled())) {
|
|
41
|
+
return { ok: false, directive: maestroInstallDirective() };
|
|
42
|
+
}
|
|
43
|
+
const java = await runCommand("java", ["-version"], process.cwd(), undefined, undefined, 20_000);
|
|
44
|
+
if (java.exit_code !== 0) {
|
|
45
|
+
return { ok: false, directive: `Maestro is installed but Java is not runnable (\`java -version\` failed). ${javaInstallDirective()}` };
|
|
46
|
+
}
|
|
47
|
+
return { ok: true };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Env for Maestro runs: the FIRST run on an iOS simulator installs +
|
|
51
|
+
* boots the XCTest driver, which routinely exceeds Maestro's default
|
|
52
|
+
* 15s driver-startup timeout — raise it so first runs don't flake.
|
|
53
|
+
*/
|
|
54
|
+
export function maestroRunEnv() {
|
|
55
|
+
return { MAESTRO_DRIVER_STARTUP_TIMEOUT: "120000" };
|
|
56
|
+
}
|
|
10
57
|
export function findMaestroFlows(cwd) {
|
|
11
58
|
const flows = [];
|
|
12
59
|
for (const rel of MAESTRO_FLOW_REL_DIRS) {
|
|
@@ -104,7 +151,7 @@ function collectScreenshotPaths(root) {
|
|
|
104
151
|
walk(root);
|
|
105
152
|
return paths.sort();
|
|
106
153
|
}
|
|
107
|
-
export async function runMaestroTests(cwd, logDir) {
|
|
154
|
+
export async function runMaestroTests(cwd, logDir, device) {
|
|
108
155
|
if (!(await isMaestroInstalled())) {
|
|
109
156
|
return emptyMaestroResult();
|
|
110
157
|
}
|
|
@@ -116,10 +163,12 @@ export async function runMaestroTests(cwd, logDir) {
|
|
|
116
163
|
const testOutputDir = join(logDir, "maestro-output");
|
|
117
164
|
mkdirSync(logDir, { recursive: true });
|
|
118
165
|
mkdirSync(testOutputDir, { recursive: true });
|
|
119
|
-
|
|
166
|
+
// `--udid` (root-level flag, alias --device) pins the run when several
|
|
167
|
+
// devices are connected — without it Maestro picks its own.
|
|
168
|
+
const args = [...(device ? ["--udid", device] : []), "test", "--test-output-dir", testOutputDir, ...flows];
|
|
120
169
|
// Cap Maestro: flows drive a real device/emulator and can stall indefinitely
|
|
121
170
|
// waiting for one to boot or for an element that never appears.
|
|
122
|
-
const result = await runCommand("maestro", args, cwd, logPath,
|
|
171
|
+
const result = await runCommand("maestro", args, cwd, logPath, maestroRunEnv(), MAESTRO_TIMEOUT_MS);
|
|
123
172
|
const combined = result.stdout + result.stderr;
|
|
124
173
|
const parsed = parseMaestroSummary(combined, flows.length, result.exit_code);
|
|
125
174
|
const screenshotPaths = collectScreenshotPaths(testOutputDir);
|
|
@@ -144,17 +193,18 @@ export async function runMaestroTests(cwd, logDir) {
|
|
|
144
193
|
* `passed` flows count toward `passed`, `failed` flows toward `failed`,
|
|
145
194
|
* and `skipped` is always 0 (Maestro doesn't surface a skipped count).
|
|
146
195
|
*/
|
|
147
|
-
export async function runMaestroFlows(cwd, logPath) {
|
|
196
|
+
export async function runMaestroFlows(cwd, logPath, device) {
|
|
148
197
|
const flows = findMaestroFlows(cwd);
|
|
149
198
|
if (flows.length === 0) {
|
|
150
199
|
return makeSkippedResult("maestro_flows", "No Maestro flows found (looked under .maestro/, tests/maestro/, maestro/)");
|
|
151
200
|
}
|
|
152
|
-
|
|
153
|
-
|
|
201
|
+
const preflight = await maestroPreflight();
|
|
202
|
+
if (!preflight.ok) {
|
|
203
|
+
return makeSkippedResult("maestro_flows", preflight.directive ?? "Maestro toolchain unavailable.");
|
|
154
204
|
}
|
|
155
205
|
const start = Date.now();
|
|
156
206
|
const logDir = join(logPath, "..");
|
|
157
|
-
const result = await runMaestroTests(cwd, logDir);
|
|
207
|
+
const result = await runMaestroTests(cwd, logDir, device);
|
|
158
208
|
const duration_ms = Date.now() - start;
|
|
159
209
|
return {
|
|
160
210
|
runner_name: "maestro_flows",
|