codeloop-mcp-server 0.1.79 → 0.1.83

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/evidence/agent_mode.d.ts +7 -2
  2. package/dist/evidence/agent_mode.d.ts.map +1 -1
  3. package/dist/evidence/agent_mode.js +63 -27
  4. package/dist/evidence/agent_mode.js.map +1 -1
  5. package/dist/evidence/interaction_evidence.d.ts +1 -1
  6. package/dist/evidence/interaction_evidence.d.ts.map +1 -1
  7. package/dist/evidence/interaction_evidence.js +3 -2
  8. package/dist/evidence/interaction_evidence.js.map +1 -1
  9. package/dist/index.js +22 -23
  10. package/dist/index.js.map +1 -1
  11. package/dist/runners/app_launcher.d.ts.map +1 -1
  12. package/dist/runners/app_launcher.js +148 -8
  13. package/dist/runners/app_launcher.js.map +1 -1
  14. package/dist/runners/device_probe.d.ts +32 -0
  15. package/dist/runners/device_probe.d.ts.map +1 -1
  16. package/dist/runners/device_probe.js +73 -0
  17. package/dist/runners/device_probe.js.map +1 -1
  18. package/dist/runners/flutter_driver.d.ts +37 -0
  19. package/dist/runners/flutter_driver.d.ts.map +1 -0
  20. package/dist/runners/flutter_driver.js +242 -0
  21. package/dist/runners/flutter_driver.js.map +1 -0
  22. package/dist/runners/journey_to_maestro.d.ts.map +1 -1
  23. package/dist/runners/journey_to_maestro.js +39 -8
  24. package/dist/runners/journey_to_maestro.js.map +1 -1
  25. package/dist/runners/launch_liveness.d.ts +44 -0
  26. package/dist/runners/launch_liveness.d.ts.map +1 -0
  27. package/dist/runners/launch_liveness.js +145 -0
  28. package/dist/runners/launch_liveness.js.map +1 -0
  29. package/dist/runners/maestro_generator.d.ts +7 -0
  30. package/dist/runners/maestro_generator.d.ts.map +1 -1
  31. package/dist/runners/maestro_generator.js +58 -0
  32. package/dist/runners/maestro_generator.js.map +1 -1
  33. package/dist/runners/mobile_build_prep.d.ts +66 -0
  34. package/dist/runners/mobile_build_prep.d.ts.map +1 -0
  35. package/dist/runners/mobile_build_prep.js +285 -0
  36. package/dist/runners/mobile_build_prep.js.map +1 -0
  37. package/dist/tools/gate_check.d.ts +15 -1
  38. package/dist/tools/gate_check.d.ts.map +1 -1
  39. package/dist/tools/gate_check.js +18 -11
  40. package/dist/tools/gate_check.js.map +1 -1
  41. package/dist/tools/run_journey.d.ts +19 -5
  42. package/dist/tools/run_journey.d.ts.map +1 -1
  43. package/dist/tools/run_journey.js +133 -39
  44. package/dist/tools/run_journey.js.map +1 -1
  45. package/dist/tools/verify.d.ts.map +1 -1
  46. package/dist/tools/verify.js +9 -16
  47. package/dist/tools/verify.js.map +1 -1
  48. package/package.json +2 -2
@@ -0,0 +1,242 @@
1
+ /**
2
+ * flutter_driver — the OPT-IN, high-fidelity Flutter interaction path.
3
+ *
4
+ * Maestro (the default) matches by visible text / accessibility label, so a
5
+ * fully-unlabeled Flutter canvas can miss a `tapOn`. When the user opts in with
6
+ * `e2e.flutter_driver: true`, CodeLoop instead SCAFFOLDS a generated
7
+ * integration_test driver that drives the app via widget finders (find.text /
8
+ * find.byKey / widgetWithText) — deterministic, no label-matching guesswork.
9
+ *
10
+ * IMPORTANT: the generated driver is written into artifacts/runs/<id>/ —
11
+ * NEVER into the user's test/ or lib/. It imports the app via its package name
12
+ * (`package:<name>/main.dart`) and is run with `flutter test <path> -d <device>`.
13
+ * Requires the project to already depend on `integration_test`; when it does
14
+ * not, CodeLoop degrades to a one-line directive (it never edits the user's
15
+ * pubspec) and the caller falls back to Maestro.
16
+ *
17
+ * Pure helpers (flutterPackageName / hasIntegrationTestDep /
18
+ * generateFlutterDriverSource) are unit-tested; the runner degrades gracefully.
19
+ */
20
+ import { mkdirSync, readFileSync, writeFileSync } from "fs";
21
+ import { join } from "path";
22
+ import { runCommand, checkToolAvailable } from "./base.js";
23
+ /** PURE: the Flutter package name from pubspec.yaml's top-level `name:`. */
24
+ export function flutterPackageName(cwd) {
25
+ try {
26
+ const pub = readFileSync(join(cwd, "pubspec.yaml"), "utf-8");
27
+ const m = pub.match(/^name:\s*([A-Za-z0-9_]+)\s*$/m);
28
+ return m ? m[1] : null;
29
+ }
30
+ catch {
31
+ return null;
32
+ }
33
+ }
34
+ /** PURE: does pubspec.yaml declare the integration_test dependency? */
35
+ export function hasIntegrationTestDep(pubspecText) {
36
+ return /^\s*integration_test\s*:/m.test(pubspecText);
37
+ }
38
+ /** Dart string-literal escape for a single-quoted literal. */
39
+ function dartLit(s) {
40
+ return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\$/g, "\\$").replace(/[\r\n]+/g, " ").slice(0, 200);
41
+ }
42
+ function usable(s) {
43
+ if (!s)
44
+ return null;
45
+ const t = s.trim().replace(/[\s:*]+$/, "").trim();
46
+ if (!t || t.length > 60)
47
+ return null;
48
+ if (/^[#.[]|[{}<>]/.test(t))
49
+ return null;
50
+ if (!/[A-Za-z0-9]/.test(t))
51
+ return null;
52
+ return t;
53
+ }
54
+ /**
55
+ * PURE: generate the Dart integration_test source that drives the journey via
56
+ * widget finders. Every action is wrapped in a soft helper so a single missing
57
+ * widget never aborts the run (it logs + continues), and a screenshot is taken
58
+ * after each meaningful step.
59
+ */
60
+ export function generateFlutterDriverSource(plan, packageName, opts = {}) {
61
+ const maxEntities = opts.maxEntities ?? 3;
62
+ const body = [];
63
+ let shot = 0;
64
+ const screenshot = () => body.push(` await _shot(binding, 'codeloop_${shot++}');`);
65
+ screenshot();
66
+ const entities = [...(plan.entities ?? [])]
67
+ .sort((a, b) => (a.priority ?? 99) - (b.priority ?? 99))
68
+ .slice(0, maxEntities);
69
+ for (const entity of entities) {
70
+ for (const step of entity.arc ?? [])
71
+ emitDart(step, body, screenshot);
72
+ }
73
+ for (const ai of plan.ai_journeys ?? []) {
74
+ const arc = ai.arc ?? [];
75
+ const promptStep = arc.find((s) => s.step === "ai_prompt" && s.ai_prompt);
76
+ const prompt = promptStep?.ai_prompt ?? ai.prompt_examples?.[0];
77
+ if (!prompt)
78
+ continue;
79
+ const nav = usable(arc.find((s) => s.step === "navigate")?.target);
80
+ if (nav)
81
+ body.push(` await _tap(tester, '${dartLit(nav)}');`);
82
+ body.push(` await _typeFirstField(tester, '${dartLit(prompt)}');`);
83
+ body.push(" await tester.pumpAndSettle(const Duration(seconds: 4));");
84
+ // Capture the AI reply text deterministically so the agent can judge it.
85
+ body.push(` _captureReply(tester, '${dartLit(prompt)}');`);
86
+ screenshot();
87
+ }
88
+ return `// GENERATED BY CODELOOP — do not edit. Scaffolded into artifacts/, not your source.
89
+ import 'package:flutter/material.dart';
90
+ import 'package:flutter_test/flutter_test.dart';
91
+ import 'package:integration_test/integration_test.dart';
92
+ import 'package:${packageName}/main.dart' as app;
93
+
94
+ Future<void> _shot(IntegrationTestWidgetsFlutterBinding binding, String name) async {
95
+ try { await binding.takeScreenshot(name); } catch (_) {}
96
+ }
97
+
98
+ Future<void> _tap(WidgetTester tester, String label) async {
99
+ try {
100
+ final f = find.text(label);
101
+ if (tester.any(f)) { await tester.tap(f.first); await tester.pumpAndSettle(); }
102
+ } catch (_) {}
103
+ }
104
+
105
+ Future<void> _typeInto(WidgetTester tester, String label, String value) async {
106
+ try {
107
+ final f = find.widgetWithText(TextField, label);
108
+ if (tester.any(f)) { await tester.enterText(f.first, value); await tester.pumpAndSettle(); return; }
109
+ } catch (_) {}
110
+ await _typeFirstField(tester, value);
111
+ }
112
+
113
+ Future<void> _typeFirstField(WidgetTester tester, String value) async {
114
+ try {
115
+ final f = find.byType(TextField);
116
+ if (tester.any(f)) { await tester.enterText(f.first, value); await tester.pumpAndSettle(); }
117
+ } catch (_) {}
118
+ }
119
+
120
+ Future<void> _expect(WidgetTester tester, String label) async {
121
+ // Soft expectation — logs, never aborts the driver.
122
+ try { if (!tester.any(find.text(label))) debugPrint('CODELOOP: not visible: ' + label); } catch (_) {}
123
+ }
124
+
125
+ void _captureReply(WidgetTester tester, String prompt) {
126
+ // Heuristic: the AI reply is the longest on-screen Text that isn't the prompt.
127
+ try {
128
+ String best = '';
129
+ for (final e in find.byType(Text).evaluate()) {
130
+ final w = e.widget;
131
+ final t = (w is Text ? (w.data ?? '') : '').trim();
132
+ if (t.isEmpty || t == prompt) continue;
133
+ if (t.length > best.length) best = t;
134
+ }
135
+ if (best.isNotEmpty) debugPrint('CODELOOP_REPLY: ' + best);
136
+ } catch (_) {}
137
+ }
138
+
139
+ void main() {
140
+ final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
141
+ testWidgets('codeloop_journey', (tester) async {
142
+ app.main();
143
+ await tester.pumpAndSettle(const Duration(seconds: 3));
144
+ ${body.join("\n")}
145
+ });
146
+ }
147
+ `;
148
+ }
149
+ function emitDart(step, body, screenshot) {
150
+ switch (step.step) {
151
+ case "navigate":
152
+ case "create":
153
+ case "commit":
154
+ case "edit":
155
+ case "delete": {
156
+ const label = usable(step.target);
157
+ if (label) {
158
+ body.push(` await _tap(tester, '${dartLit(label)}');`);
159
+ screenshot();
160
+ }
161
+ break;
162
+ }
163
+ case "fill": {
164
+ for (const f of step.fields ?? []) {
165
+ const label = usable(f.label) ?? usable(f.selector);
166
+ const value = f.example != null ? String(f.example) : "";
167
+ if (label && value)
168
+ body.push(` await _typeInto(tester, '${dartLit(label)}', '${dartLit(value)}');`);
169
+ }
170
+ screenshot();
171
+ break;
172
+ }
173
+ case "verify": {
174
+ const label = usable(step.target);
175
+ if (label)
176
+ body.push(` await _expect(tester, '${dartLit(label)}');`);
177
+ break;
178
+ }
179
+ default:
180
+ break;
181
+ }
182
+ }
183
+ /** PURE: extract the captured AI reply from driver output (CODELOOP_REPLY:). */
184
+ export function parseDriverReply(output) {
185
+ const lines = output.split(/\r?\n/);
186
+ for (let i = lines.length - 1; i >= 0; i--) {
187
+ const m = lines[i].match(/CODELOOP_REPLY:\s*(.+)$/);
188
+ if (m && m[1].trim())
189
+ return m[1].trim();
190
+ }
191
+ return undefined;
192
+ }
193
+ const DRIVER_TIMEOUT_MS = 600_000;
194
+ /**
195
+ * Scaffold + run the integration_test driver on the booted device. Degrades to
196
+ * ran:false + a directive (never edits user source) when prerequisites are
197
+ * missing, so the caller can fall back to Maestro.
198
+ */
199
+ export async function runFlutterDriver(args) {
200
+ const { cwd, plan, device, runDir } = args;
201
+ const pkg = flutterPackageName(cwd);
202
+ if (!pkg)
203
+ return { ran: false, passed: false, directive: "Could not read the Flutter package name from pubspec.yaml — falling back to Maestro." };
204
+ let pubText = "";
205
+ try {
206
+ pubText = readFileSync(join(cwd, "pubspec.yaml"), "utf-8");
207
+ }
208
+ catch { /* ignore */ }
209
+ if (!hasIntegrationTestDep(pubText)) {
210
+ return {
211
+ ran: false,
212
+ passed: false,
213
+ directive: "e2e.flutter_driver is enabled but the project has no `integration_test` dependency. " +
214
+ "Add it (dev_dependencies: integration_test: { sdk: flutter }) and re-run, or rely on the default Maestro driver.",
215
+ };
216
+ }
217
+ if (!(await checkToolAvailable("flutter"))) {
218
+ return { ran: false, passed: false, directive: "flutter not on PATH — cannot run the integration_test driver." };
219
+ }
220
+ const driverDir = join(runDir, "flutter_driver");
221
+ try {
222
+ mkdirSync(driverDir, { recursive: true });
223
+ }
224
+ catch { /* best-effort */ }
225
+ const testPath = join(driverDir, "codeloop_journey_test.dart");
226
+ try {
227
+ writeFileSync(testPath, generateFlutterDriverSource(plan, pkg));
228
+ }
229
+ catch (e) {
230
+ return { ran: false, passed: false, directive: `Could not scaffold the driver test: ${e.message}` };
231
+ }
232
+ const res = await runCommand("flutter", ["test", testPath, "-d", device], cwd, join(runDir, "logs", "flutter_driver.log"), undefined, DRIVER_TIMEOUT_MS);
233
+ const combined = res.stdout + "\n" + res.stderr;
234
+ return {
235
+ ran: true,
236
+ passed: res.exit_code === 0,
237
+ test_path: testPath,
238
+ output: combined.split("\n").filter(Boolean).slice(-25).join("\n"),
239
+ ai_reply: parseDriverReply(combined),
240
+ };
241
+ }
242
+ //# sourceMappingURL=flutter_driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flutter_driver.js","sourceRoot":"","sources":["../../src/runners/flutter_driver.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"journey_to_maestro.d.ts","sourceRoot":"","sources":["../../src/runners/journey_to_maestro.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,eAAe,EAAe,MAAM,+BAA+B,CAAC;AAElF,MAAM,WAAW,uBAAuB;IACtC,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AA2DD;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,eAAe,EACrB,IAAI,GAAE,uBAA4B,GACjC,MAAM,EAAE,CAkCV"}
1
+ {"version":3,"file":"journey_to_maestro.d.ts","sourceRoot":"","sources":["../../src/runners/journey_to_maestro.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,eAAe,EAAe,MAAM,+BAA+B,CAAC;AAElF,MAAM,WAAW,uBAAuB;IACtC,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAiFD;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,eAAe,EACrB,IAAI,GAAE,uBAA4B,GACjC,MAAM,EAAE,CA8CV"}
@@ -2,7 +2,9 @@
2
2
  function usableLabel(s) {
3
3
  if (!s)
4
4
  return null;
5
- const t = s.trim();
5
+ // Trim and drop common trailing field punctuation (`Email:` / `Name *`) that
6
+ // wouldn't be part of the on-screen text Maestro matches against.
7
+ const t = s.trim().replace(/[\s:*]+$/, "").trim();
6
8
  // Reject empties, very long blobs, CSS/selectors, and pure punctuation.
7
9
  if (!t || t.length > 60)
8
10
  return null;
@@ -12,6 +14,16 @@ function usableLabel(s) {
12
14
  return null;
13
15
  return t;
14
16
  }
17
+ /**
18
+ * Infer the best usable label for a fill field: prefer the human label, else
19
+ * the last path segment of a selector, else the hint. Improves Flutter hit-rate
20
+ * without inventing text (each candidate came from the plan).
21
+ */
22
+ function fieldLabel(f) {
23
+ return (usableLabel(f.label) ??
24
+ usableLabel(f.selector?.split(/[#.\s>]/).filter(Boolean).pop()) ??
25
+ usableLabel(f.hint));
26
+ }
15
27
  /** Maestro inputText cannot contain a double-quote unescaped; sanitise. */
16
28
  function safeText(s) {
17
29
  return s.replace(/"/g, "'").replace(/\s+/g, " ").trim().slice(0, 200);
@@ -25,7 +37,12 @@ function emitEntityStep(step, out, shots) {
25
37
  case "delete": {
26
38
  const label = usableLabel(step.target);
27
39
  if (label) {
28
- out.push(`tap "${label}"`);
40
+ // OPTIONAL tap: a mislabeled/missing button must NOT abort the whole
41
+ // flow — the rest of the journey + screenshots still run, which is the
42
+ // dominant Flutter hit-rate win (Maestro matches visible text/labels).
43
+ out.push(`tap optional "${label}"`);
44
+ // Let Flutter finish its route/animation before the next match.
45
+ out.push("wait for idle");
29
46
  if (shots)
30
47
  out.push("take screenshot");
31
48
  }
@@ -33,11 +50,11 @@ function emitEntityStep(step, out, shots) {
33
50
  }
34
51
  case "fill": {
35
52
  for (const f of step.fields ?? []) {
36
- const label = usableLabel(f.label);
53
+ const label = fieldLabel(f);
37
54
  const value = f.example != null ? safeText(String(f.example)) : "";
38
55
  if (label && value) {
39
56
  // Tap the field by its label, then type — scopes inputText to it.
40
- out.push(`tap "${label}"`);
57
+ out.push(`tap optional "${label}"`);
41
58
  out.push(`type "${value}"`);
42
59
  }
43
60
  }
@@ -47,8 +64,10 @@ function emitEntityStep(step, out, shots) {
47
64
  }
48
65
  case "verify": {
49
66
  const label = usableLabel(step.target);
67
+ // OPTIONAL assert: records the expectation (and waits) without killing the
68
+ // flow if the label isn't matched, so later screens are still captured.
50
69
  if (label)
51
- out.push(`assert visible "${label}" timeout 8000`);
70
+ out.push(`assert visible optional "${label}"`);
52
71
  break;
53
72
  }
54
73
  case "upload":
@@ -88,11 +107,23 @@ export function journeyToMaestroSteps(plan, opts = {}) {
88
107
  continue;
89
108
  // If the arc names an input target/label, tap it first to focus.
90
109
  const navTarget = usableLabel(arc.find((s) => s.step === "navigate")?.target);
91
- if (navTarget)
92
- out.push(`tap "${navTarget}"`);
110
+ if (navTarget) {
111
+ out.push(`tap optional "${navTarget}"`);
112
+ out.push("wait for idle");
113
+ }
93
114
  out.push(`type "${safeText(prompt)}"`);
94
115
  out.push("press enter");
95
- out.push(`wait ${Math.max(1, aiWait) * 1000}`); // Maestro timeout is in ms
116
+ // If the arc names a reply region/label, WAIT for it to render (async/AI
117
+ // replies take a moment) and CAPTURE its text so the agent can judge the
118
+ // answer's correctness — not just that something appeared.
119
+ const replyLabel = usableLabel(arc.find((s) => s.step === "verify")?.target);
120
+ if (replyLabel) {
121
+ out.push(`assert visible "${replyLabel}" timeout ${Math.max(1, aiWait) * 2000}`);
122
+ out.push(`copy text from "${replyLabel}"`);
123
+ }
124
+ else {
125
+ out.push(`wait ${Math.max(1, aiWait) * 1000}`); // Maestro timeout is in ms
126
+ }
96
127
  if (shots)
97
128
  out.push("take screenshot");
98
129
  }
@@ -1 +1 @@
1
- {"version":3,"file":"journey_to_maestro.js","sourceRoot":"","sources":["../../src/runners/journey_to_maestro.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"journey_to_maestro.js","sourceRoot":"","sources":["../../src/runners/journey_to_maestro.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import type { JourneyTarget } from "./interaction_engine.js";
2
+ /** PURE: `adb shell pidof <pkg>` prints a PID (or nothing). Non-empty => alive. */
3
+ export declare function parsePidofPresent(stdout: string): boolean;
4
+ /**
5
+ * PURE: does the Android window manager report our app as the focused window?
6
+ * Looks at `dumpsys window` mCurrentFocus / mFocusedApp lines for the appId.
7
+ */
8
+ export declare function isForegrounded(dumpsysText: string, appId: string): boolean;
9
+ /** PURE: is the iOS app present in the simulator's launchd job list? */
10
+ export declare function isAppInLaunchctl(launchctlText: string, bundleId: string): boolean;
11
+ /**
12
+ * PURE: is this RGBA pixel buffer effectively a single flat colour (a blank /
13
+ * all-black frame)? Samples a stride of pixels and checks per-channel spread.
14
+ * `tolerance` is the max (max-min) range per channel that still counts as flat.
15
+ */
16
+ export declare function isUniformImage(rgba: Uint8Array | Buffer, opts?: {
17
+ channels?: number;
18
+ stride?: number;
19
+ tolerance?: number;
20
+ }): boolean;
21
+ /**
22
+ * Decode a PNG and report whether it is a blank/uniform frame. Best-effort:
23
+ * returns false (NOT blank) if the file is missing or can't be decoded, so a
24
+ * decode hiccup never blocks a real launch.
25
+ */
26
+ export declare function isBlankFrame(pngPath: string): Promise<boolean>;
27
+ export interface LivenessResult {
28
+ live: boolean;
29
+ foreground: boolean;
30
+ detail: string;
31
+ }
32
+ /**
33
+ * Verify the mobile app process is actually running (and, where cheap to check,
34
+ * foregrounded) on the booted device. Degrades to live:true when the probe
35
+ * tooling is unavailable, so a missing `adb`/`xcrun` never blocks the journey —
36
+ * the blank-frame check is the second line of defence in that case.
37
+ */
38
+ export declare function verifyMobileAppLive(target: JourneyTarget, device: string | undefined, appId: string | null): Promise<LivenessResult>;
39
+ /**
40
+ * Bring the device window to the front before screenshotting/driving, so the
41
+ * captured frame is the APP, not the IDE/desktop. Best-effort, never throws.
42
+ */
43
+ export declare function bringDeviceToFront(target: JourneyTarget): Promise<void>;
44
+ //# sourceMappingURL=launch_liveness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launch_liveness.d.ts","sourceRoot":"","sources":["../../src/runners/launch_liveness.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAK7D,mFAAmF;AACnF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGzD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAM1E;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAIjF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,UAAU,GAAG,MAAM,EACzB,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACpE,OAAO,CAqBT;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAUpE;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,GACnB,OAAO,CAAC,cAAc,CAAC,CA6BzB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ7E"}
@@ -0,0 +1,145 @@
1
+ /**
2
+ * launch_liveness — post-launch PROOF that the app is actually running and
3
+ * showing a real frame, not just that a "ready" log line appeared.
4
+ *
5
+ * The WedCheese "the app never opened" failure had two silent modes:
6
+ * 1. `flutter run` printed (or we missed) a ready line, but the app process
7
+ * was not actually live / foregrounded on the device.
8
+ * 2. The device booted to a BLANK (all-black) screen and we drove / screenshot
9
+ * the desktop or an empty surface as if it were the app.
10
+ *
11
+ * This module verifies LIVENESS (process running + foregrounded) and rejects a
12
+ * BLANK first frame, so the launcher can retry once before declaring failure.
13
+ *
14
+ * Pure helpers (parsePidofPresent / isForegrounded / isAppInLaunchctl /
15
+ * isUniformImage) are unit-tested; the device/image calls degrade gracefully.
16
+ */
17
+ import { existsSync } from "fs";
18
+ import { readFile } from "fs/promises";
19
+ import { runCommand } from "./base.js";
20
+ const PROBE_TIMEOUT_MS = 15_000;
21
+ /** PURE: `adb shell pidof <pkg>` prints a PID (or nothing). Non-empty => alive. */
22
+ export function parsePidofPresent(stdout) {
23
+ const t = stdout.trim();
24
+ return t.length > 0 && /\d/.test(t);
25
+ }
26
+ /**
27
+ * PURE: does the Android window manager report our app as the focused window?
28
+ * Looks at `dumpsys window` mCurrentFocus / mFocusedApp lines for the appId.
29
+ */
30
+ export function isForegrounded(dumpsysText, appId) {
31
+ if (!appId)
32
+ return false;
33
+ const focusLines = dumpsysText
34
+ .split(/\r?\n/)
35
+ .filter((l) => /mCurrentFocus|mFocusedApp|mResumedActivity|topResumedActivity/i.test(l));
36
+ return focusLines.some((l) => l.includes(appId));
37
+ }
38
+ /** PURE: is the iOS app present in the simulator's launchd job list? */
39
+ export function isAppInLaunchctl(launchctlText, bundleId) {
40
+ if (!bundleId)
41
+ return false;
42
+ // simctl labels are like `UIKitApplication:com.acme.app[...]` — substring is enough.
43
+ return launchctlText.includes(bundleId);
44
+ }
45
+ /**
46
+ * PURE: is this RGBA pixel buffer effectively a single flat colour (a blank /
47
+ * all-black frame)? Samples a stride of pixels and checks per-channel spread.
48
+ * `tolerance` is the max (max-min) range per channel that still counts as flat.
49
+ */
50
+ export function isUniformImage(rgba, opts = {}) {
51
+ const channels = opts.channels ?? 4;
52
+ const tolerance = opts.tolerance ?? 6;
53
+ const totalPixels = Math.floor(rgba.length / channels);
54
+ if (totalPixels === 0)
55
+ return true;
56
+ // Sample ~2000 pixels evenly across the buffer.
57
+ const stride = opts.stride ?? Math.max(1, Math.floor(totalPixels / 2000));
58
+ const min = [255, 255, 255];
59
+ const max = [0, 0, 0];
60
+ let sampled = 0;
61
+ for (let p = 0; p < totalPixels; p += stride) {
62
+ const base = p * channels;
63
+ for (let c = 0; c < 3; c++) {
64
+ const v = rgba[base + c];
65
+ if (v < min[c])
66
+ min[c] = v;
67
+ if (v > max[c])
68
+ max[c] = v;
69
+ }
70
+ sampled++;
71
+ }
72
+ if (sampled === 0)
73
+ return true;
74
+ return [0, 1, 2].every((c) => max[c] - min[c] <= tolerance);
75
+ }
76
+ /**
77
+ * Decode a PNG and report whether it is a blank/uniform frame. Best-effort:
78
+ * returns false (NOT blank) if the file is missing or can't be decoded, so a
79
+ * decode hiccup never blocks a real launch.
80
+ */
81
+ export async function isBlankFrame(pngPath) {
82
+ try {
83
+ if (!pngPath || !existsSync(pngPath))
84
+ return false;
85
+ const { PNG } = await import("pngjs");
86
+ const buf = await readFile(pngPath);
87
+ const png = PNG.sync.read(buf);
88
+ return isUniformImage(png.data, { channels: 4 });
89
+ }
90
+ catch {
91
+ return false;
92
+ }
93
+ }
94
+ /**
95
+ * Verify the mobile app process is actually running (and, where cheap to check,
96
+ * foregrounded) on the booted device. Degrades to live:true when the probe
97
+ * tooling is unavailable, so a missing `adb`/`xcrun` never blocks the journey —
98
+ * the blank-frame check is the second line of defence in that case.
99
+ */
100
+ export async function verifyMobileAppLive(target, device, appId) {
101
+ if (!appId)
102
+ return { live: true, foreground: false, detail: "no appId to probe — skipped liveness check" };
103
+ if (target === "android_emulator") {
104
+ const serial = device ? ["-s", device] : [];
105
+ const pid = await runCommand("adb", [...serial, "shell", "pidof", appId], process.cwd(), undefined, undefined, PROBE_TIMEOUT_MS);
106
+ if (pid.exit_code === 127)
107
+ return { live: true, foreground: false, detail: "adb unavailable — liveness skipped" };
108
+ const live = parsePidofPresent(pid.stdout);
109
+ let foreground = false;
110
+ try {
111
+ const dump = await runCommand("adb", [...serial, "shell", "dumpsys", "window"], process.cwd(), undefined, undefined, PROBE_TIMEOUT_MS);
112
+ foreground = isForegrounded(dump.stdout, appId);
113
+ }
114
+ catch { /* best-effort */ }
115
+ return {
116
+ live,
117
+ foreground,
118
+ detail: live ? `process ${appId} running${foreground ? " and foregrounded" : ""}` : `process ${appId} not running`,
119
+ };
120
+ }
121
+ if (target === "ios_simulator") {
122
+ const udid = device ?? "booted";
123
+ const list = await runCommand("xcrun", ["simctl", "spawn", udid, "launchctl", "list"], process.cwd(), undefined, undefined, PROBE_TIMEOUT_MS);
124
+ if (list.exit_code === 127)
125
+ return { live: true, foreground: false, detail: "xcrun unavailable — liveness skipped" };
126
+ const live = isAppInLaunchctl(list.stdout, appId);
127
+ return { live, foreground: live, detail: live ? `${appId} present in simulator launchd` : `${appId} not running in simulator` };
128
+ }
129
+ return { live: true, foreground: true, detail: "non-mobile target — liveness check n/a" };
130
+ }
131
+ /**
132
+ * Bring the device window to the front before screenshotting/driving, so the
133
+ * captured frame is the APP, not the IDE/desktop. Best-effort, never throws.
134
+ */
135
+ export async function bringDeviceToFront(target) {
136
+ try {
137
+ if (target === "ios_simulator" && process.platform === "darwin") {
138
+ await runCommand("open", ["-a", "Simulator"], process.cwd(), undefined, undefined, PROBE_TIMEOUT_MS);
139
+ }
140
+ // Android emulator + others: the emulator window is already frontmost after
141
+ // boot; no reliable cross-platform front-bring without extra tooling.
142
+ }
143
+ catch { /* best-effort */ }
144
+ }
145
+ //# sourceMappingURL=launch_liveness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launch_liveness.js","sourceRoot":"","sources":["../../src/runners/launch_liveness.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,13 @@ export declare function generateMaestroFlow(steps: string[], cwd: string, opts?:
7
7
  error: string;
8
8
  }>;
9
9
  export declare function stepToYaml(step: string): string;
10
+ /**
11
+ * PURE: summarise which Maestro commands FAILED from its output, so the agent
12
+ * sees exactly which label(s) didn't match (e.g. an unlabeled Flutter widget)
13
+ * instead of a raw log tail. Never invents labels — only echoes what Maestro
14
+ * reported. Returns [] when nothing failed / nothing recognizable.
15
+ */
16
+ export declare function summarizeMaestroFailures(output: string): string[];
10
17
  export declare function runGeneratedFlow(flowPath: string, cwd: string): Promise<{
11
18
  success: boolean;
12
19
  output: string;
@@ -1 +1 @@
1
- {"version":3,"file":"maestro_generator.d.ts","sourceRoot":"","sources":["../../src/runners/maestro_generator.ts"],"names":[],"mappings":"AAIA,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,MAAM,EAAE,EACf,GAAG,EAAE,MAAM,EACX,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAChD,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAenD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAkD/C;AAED,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAe/D"}
1
+ {"version":3,"file":"maestro_generator.d.ts","sourceRoot":"","sources":["../../src/runners/maestro_generator.ts"],"names":[],"mappings":"AAIA,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,MAAM,EAAE,EACf,GAAG,EAAE,MAAM,EACX,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAChD,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAenD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAsE/C;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAsBjE;AAED,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAe/D"}