codeloop-mcp-server 0.1.50 → 0.1.51

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 (47) hide show
  1. package/dist/auth/critical_floors.d.ts.map +1 -1
  2. package/dist/auth/critical_floors.js +4 -0
  3. package/dist/auth/critical_floors.js.map +1 -1
  4. package/dist/evidence/loop_state.d.ts +53 -0
  5. package/dist/evidence/loop_state.d.ts.map +1 -0
  6. package/dist/evidence/loop_state.js +147 -0
  7. package/dist/evidence/loop_state.js.map +1 -0
  8. package/dist/evidence/verify_staleness.d.ts +9 -0
  9. package/dist/evidence/verify_staleness.d.ts.map +1 -0
  10. package/dist/evidence/verify_staleness.js +180 -0
  11. package/dist/evidence/verify_staleness.js.map +1 -0
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +252 -17
  15. package/dist/index.js.map +1 -1
  16. package/dist/runners/maestro.d.ts +13 -0
  17. package/dist/runners/maestro.d.ts.map +1 -1
  18. package/dist/runners/maestro.js +37 -1
  19. package/dist/runners/maestro.js.map +1 -1
  20. package/dist/runners/modal_detector.d.ts +60 -0
  21. package/dist/runners/modal_detector.d.ts.map +1 -0
  22. package/dist/runners/modal_detector.js +160 -0
  23. package/dist/runners/modal_detector.js.map +1 -0
  24. package/dist/runners/python_tests.d.ts +26 -0
  25. package/dist/runners/python_tests.d.ts.map +1 -0
  26. package/dist/runners/python_tests.js +181 -0
  27. package/dist/runners/python_tests.js.map +1 -0
  28. package/dist/runners/rust_tests.d.ts +28 -0
  29. package/dist/runners/rust_tests.d.ts.map +1 -0
  30. package/dist/runners/rust_tests.js +76 -0
  31. package/dist/runners/rust_tests.js.map +1 -0
  32. package/dist/tools/diagnose.d.ts.map +1 -1
  33. package/dist/tools/diagnose.js +13 -0
  34. package/dist/tools/diagnose.js.map +1 -1
  35. package/dist/tools/gate_check.d.ts +2 -1
  36. package/dist/tools/gate_check.d.ts.map +1 -1
  37. package/dist/tools/gate_check.js +46 -32
  38. package/dist/tools/gate_check.js.map +1 -1
  39. package/dist/tools/is_ui_project.d.ts +23 -0
  40. package/dist/tools/is_ui_project.d.ts.map +1 -0
  41. package/dist/tools/is_ui_project.js +42 -0
  42. package/dist/tools/is_ui_project.js.map +1 -0
  43. package/dist/tools/verify.d.ts +28 -0
  44. package/dist/tools/verify.d.ts.map +1 -1
  45. package/dist/tools/verify.js +159 -7
  46. package/dist/tools/verify.js.map +1 -1
  47. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { runCommand } from "./base.js";
1
+ import { runCommand, makeSkippedResult } from "./base.js";
2
2
  import { existsSync, mkdirSync, readdirSync } from "fs";
3
3
  import { join } from "path";
4
4
  const MAESTRO_FLOW_REL_DIRS = ["tests/maestro", ".maestro", "maestro"];
@@ -130,4 +130,40 @@ export async function runMaestroTests(cwd, logDir) {
130
130
  logPath,
131
131
  };
132
132
  }
133
+ /**
134
+ * 0.1.51 H9 — Adapt `runMaestroTests` to the `RunnerResult` shape used
135
+ * by every other verify runner so it can be pushed into the `results`
136
+ * array in `runVerify` without special-casing. Returns a skipped
137
+ * result (`available: false`) when:
138
+ * - no `.maestro/` / `tests/maestro/` / `maestro/` flows are present;
139
+ * - the `maestro` CLI is not on PATH.
140
+ *
141
+ * `passed` flows count toward `passed`, `failed` flows toward `failed`,
142
+ * and `skipped` is always 0 (Maestro doesn't surface a skipped count).
143
+ */
144
+ export async function runMaestroFlows(cwd, logPath) {
145
+ const flows = findMaestroFlows(cwd);
146
+ if (flows.length === 0) {
147
+ return makeSkippedResult("maestro_flows", "No Maestro flows found (looked under .maestro/, tests/maestro/, maestro/)");
148
+ }
149
+ if (!(await isMaestroInstalled())) {
150
+ return makeSkippedResult("maestro_flows", "maestro CLI not found on PATH. Install via `curl -Ls 'https://get.maestro.mobile.dev' | bash` (POSIX/macOS) and retry.");
151
+ }
152
+ const start = Date.now();
153
+ const logDir = join(logPath, "..");
154
+ const result = await runMaestroTests(cwd, logDir);
155
+ const duration_ms = Date.now() - start;
156
+ return {
157
+ runner_name: "maestro_flows",
158
+ available: true,
159
+ exit_code: result.passed ? 0 : 1,
160
+ passed: result.passedFlows,
161
+ failed: result.failedFlows,
162
+ skipped: 0,
163
+ log_path: result.logPath,
164
+ duration_ms,
165
+ stdout: "",
166
+ stderr: "",
167
+ };
168
+ }
133
169
  //# sourceMappingURL=maestro.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"maestro.js","sourceRoot":"","sources":["../../src/runners/maestro.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"maestro.js","sourceRoot":"","sources":["../../src/runners/maestro.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE1D,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,IAAI,CAAC;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAW5B,MAAM,qBAAqB,GAAG,CAAC,eAAe,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;AAEvE,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,SAAS,EAAE,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACzE,OAAO,MAAM,CAAC,SAAS,KAAK,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,GAAG,IAAI,qBAAqB,EAAE,CAAC;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC3B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAC/B,gBAAgB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AACpC,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAW,EAAE,GAAa;IAClD,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;YACpB,gBAAgB,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC3B,CAAC;aAAM,IAAI,CAAC,CAAC,MAAM,EAAE,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YACpD,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACd,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,OAAO,GAAG,EAAE;IACtC,OAAO;QACL,MAAM,EAAE,KAAK;QACb,UAAU,EAAE,CAAC;QACb,WAAW,EAAE,CAAC;QACd,WAAW,EAAE,CAAC;QACd,eAAe,EAAE,EAAE;QACnB,OAAO;KACR,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,CAAS;IAC1B,yDAAyD;IACzD,+EAA+E;IAC/E,OAAO,CAAC,CAAC,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC;AAC5C,CAAC;AAED,SAAS,mBAAmB,CAC1B,MAAc,EACd,SAAiB,EACjB,QAAgB;IAEhB,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IAE/B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;IACjE,IAAI,IAAI,EAAE,CAAC;QACT,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1C,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACzC,OAAO;YACL,UAAU;YACV,WAAW;YACX,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,GAAG,WAAW,CAAC;SACnD,CAAC;IACJ,CAAC;IAED,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,WAAW,GAAG,CAAC,CAAC;IAEpB,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC3D,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC3D,IAAI,WAAW,IAAI,WAAW,EAAE,CAAC;QAC/B,WAAW,GAAG,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7D,WAAW,GAAG,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7D,MAAM,KAAK,GAAG,WAAW,GAAG,WAAW,CAAC;QACxC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACd,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;QACzD,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAC7C,IAAI,MAAM,IAAI,MAAM,EAAE,CAAC;QACrB,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACnD,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACnD,MAAM,KAAK,GAAG,WAAW,GAAG,WAAW,CAAC;QACxC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACd,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;QACzD,CAAC;IACH,CAAC;IAED,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;QAClB,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;YACnB,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;QAC3E,CAAC;QACD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC;IAC3E,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;AAC3D,CAAC;AAED,SAAS,sBAAsB,CAAC,IAAY;IAC1C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,SAAS,IAAI,CAAC,GAAW;QACvB,KAAK,MAAM,CAAC,IAAI,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YAC1D,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;YAC5B,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;gBACpB,IAAI,CAAC,CAAC,CAAC,CAAC;YACV,CAAC;iBAAM,IAAI,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC/C,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAChB,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,CAAC,IAAI,CAAC,CAAC;IACX,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;AACtB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAW,EAAE,MAAc;IAC/D,IAAI,CAAC,CAAC,MAAM,kBAAkB,EAAE,CAAC,EAAE,CAAC;QAClC,OAAO,kBAAkB,EAAE,CAAC;IAC9B,CAAC;IAED,MAAM,KAAK,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,kBAAkB,EAAE,CAAC;IAC9B,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IACjD,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAErD,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,SAAS,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE9C,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,mBAAmB,EAAE,aAAa,EAAE,GAAG,KAAK,CAAC,CAAC;IACpE,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAE/D,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC/C,MAAM,MAAM,GAAG,mBAAmB,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;IAC7E,MAAM,eAAe,GAAG,sBAAsB,CAAC,aAAa,CAAC,CAAC;IAE9D,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,KAAK,CAAC,CAAC;IAEtC,OAAO;QACL,MAAM;QACN,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,eAAe;QACf,OAAO;KACR,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,GAAW,EACX,OAAe;IAEf,MAAM,KAAK,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,iBAAiB,CACtB,eAAe,EACf,2EAA2E,CAC5E,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,CAAC,MAAM,kBAAkB,EAAE,CAAC,EAAE,CAAC;QAClC,OAAO,iBAAiB,CACtB,eAAe,EACf,wHAAwH,CACzH,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACnC,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAClD,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;IAEvC,OAAO;QACL,WAAW,EAAE,eAAe;QAC5B,SAAS,EAAE,IAAI;QACf,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,EAAE,MAAM,CAAC,WAAW;QAC1B,MAAM,EAAE,MAAM,CAAC,WAAW;QAC1B,OAAO,EAAE,CAAC;QACV,QAAQ,EAAE,MAAM,CAAC,OAAO;QACxB,WAAW;QACX,MAAM,EAAE,EAAE;QACV,MAAM,EAAE,EAAE;KACX,CAAC;AACJ,CAAC"}
@@ -0,0 +1,60 @@
1
+ import type { CodeLoopConfig } from "@codelooptech/shared";
2
+ /**
3
+ * 0.1.51 H11 — Cross-platform modal detector.
4
+ *
5
+ * Detects whether the running app currently has a foreground modal
6
+ * dialog (alert / confirm / sheet / overlay) that the agent must
7
+ * interact with before proceeding. The plan exposes 4 enforcement
8
+ * layers; this module is layer 1 (the detector) plus the building
9
+ * block for layer 4 (`codeloop_handle_modal`).
10
+ *
11
+ * Detection strategy:
12
+ * - Browser: query Playwright's page for alert/confirm dialogs
13
+ * and visible overlay elements (role=dialog, aria-modal=true,
14
+ * CSS classes that match common modal libraries).
15
+ * - Windows desktop: PowerShell + UIAutomation — walk the desktop
16
+ * tree for ControlType.Window with WindowVisualState=Normal AND
17
+ * IsModal=true (or, when IsModal isn't reliable, look for the
18
+ * OWNED-window pattern + small-screen-fraction heuristic).
19
+ * - macOS desktop: AppleScript — `every window of process whose
20
+ * subrole is "AXDialog" or "AXSystemDialog"`.
21
+ * - Linux desktop: xdotool / xprop walk for windows whose WM_NAME
22
+ * contains "Dialog" / "Confirm" / "Alert" or which set
23
+ * _NET_WM_WINDOW_TYPE_DIALOG.
24
+ * - Android / iOS: best-effort via dumpsys / xcrun — these
25
+ * surfaces are noisy enough that we treat the detector as
26
+ * optional and let codeloop_handle_modal fall through to a
27
+ * manual prompt (see the H11 docs entry in the plan).
28
+ *
29
+ * The detector NEVER fails the call; on any error it returns
30
+ * `is_modal_present: false` plus a `detection_error` string so the
31
+ * agent can decide whether to retry. Agents must NOT treat a false
32
+ * negative as proof there's no modal — the post-interact directive
33
+ * (layer 3) covers that case by always nudging the agent to look.
34
+ */
35
+ export interface ModalDetectionResult {
36
+ is_modal_present: boolean;
37
+ /** Human-readable description of the detected modal, when present. */
38
+ modal_description?: string;
39
+ /** Suggested action for the agent: "confirm" | "cancel" | "dismiss" | "inspect". */
40
+ suggested_action?: string;
41
+ /** Heuristic confidence 0-1; > 0.7 should be acted on. */
42
+ confidence?: number;
43
+ /** When the detector failed (e.g. UIA not installed), set so the agent doesn't double-fire. */
44
+ detection_error?: string;
45
+ /** target_type the detector ran against. */
46
+ target_type: "browser" | "desktop" | "android_emulator" | "ios_simulator" | "unknown";
47
+ }
48
+ export declare function detectModal(opts: {
49
+ target_type?: "browser" | "desktop" | "android_emulator" | "ios_simulator";
50
+ app_name?: string;
51
+ cwd: string;
52
+ config?: CodeLoopConfig;
53
+ }): Promise<ModalDetectionResult>;
54
+ /**
55
+ * Heuristic for whether a given action is "modal-related" — used by
56
+ * layer 2 (pre-interact block) to decide whether an unrelated action
57
+ * should be refused while a modal is open.
58
+ */
59
+ export declare function isModalRelatedAction(action: string): boolean;
60
+ //# sourceMappingURL=modal_detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modal_detector.d.ts","sourceRoot":"","sources":["../../src/runners/modal_detector.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,MAAM,WAAW,oBAAoB;IACnC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,sEAAsE;IACtE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oFAAoF;IACpF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+FAA+F;IAC/F,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,4CAA4C;IAC5C,WAAW,EAAE,SAAS,GAAG,SAAS,GAAG,kBAAkB,GAAG,eAAe,GAAG,SAAS,CAAC;CACvF;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE;IACtC,WAAW,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,kBAAkB,GAAG,eAAe,CAAC;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,cAAc,CAAC;CACzB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAiBhC;AA6ID;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAiB5D"}
@@ -0,0 +1,160 @@
1
+ import { runCommand } from "./base.js";
2
+ export async function detectModal(opts) {
3
+ const target = opts.target_type ?? "desktop";
4
+ try {
5
+ if (target === "browser")
6
+ return await detectBrowserModal(opts);
7
+ if (target === "desktop")
8
+ return await detectDesktopModal(opts);
9
+ return {
10
+ is_modal_present: false,
11
+ target_type: target,
12
+ detection_error: `Modal detection on ${target} is best-effort. Use codeloop_handle_modal manually if a modal blocks the interaction.`,
13
+ };
14
+ }
15
+ catch (e) {
16
+ return {
17
+ is_modal_present: false,
18
+ target_type: target,
19
+ detection_error: e.message,
20
+ };
21
+ }
22
+ }
23
+ async function detectBrowserModal(_opts) {
24
+ // Browser detection requires the Playwright page handle. We don't
25
+ // own the page from this module — the actual probe runs through
26
+ // browser_interaction.ts at call-time. This stub returns a
27
+ // structured "use Playwright at the call site" so the wrapper in
28
+ // index.ts can short-circuit instead of re-implementing the
29
+ // browser tree walk.
30
+ return {
31
+ is_modal_present: false,
32
+ target_type: "browser",
33
+ detection_error: "Browser modal detection is performed inline by codeloop_interact via Playwright (page.locator('[role=\"dialog\"][aria-modal=\"true\"], dialog[open], .modal:not(.hidden)')). This module returns a stub for the cross-platform contract.",
34
+ };
35
+ }
36
+ async function detectDesktopModal(opts) {
37
+ if (process.platform === "win32")
38
+ return detectWindowsModal(opts);
39
+ if (process.platform === "darwin")
40
+ return detectMacModal(opts);
41
+ return detectLinuxModal(opts);
42
+ }
43
+ async function detectWindowsModal(opts) {
44
+ const app = opts.app_name ?? "";
45
+ const filterClause = app
46
+ ? `if ($name -notlike '*' + '${app.replace(/'/g, "''")}' + '*') { continue }`
47
+ : "";
48
+ // PowerShell UIA probe. Returns JSON the agent can parse.
49
+ const script = [
50
+ "Add-Type -AssemblyName UIAutomationClient",
51
+ "$root = [System.Windows.Automation.AutomationElement]::RootElement",
52
+ "$cond = New-Object System.Windows.Automation.PropertyCondition([System.Windows.Automation.AutomationElement]::ControlTypeProperty, [System.Windows.Automation.ControlType]::Window)",
53
+ "$windows = $root.FindAll([System.Windows.Automation.TreeScope]::Children, $cond)",
54
+ "$result = @()",
55
+ "foreach ($w in $windows) {",
56
+ " $name = $w.Current.Name",
57
+ " $owned = $false",
58
+ " try {",
59
+ " $pattern = $w.GetCurrentPattern([System.Windows.Automation.WindowPattern]::Pattern)",
60
+ " $owned = $pattern.Current.IsModal",
61
+ " } catch { }",
62
+ " " + filterClause,
63
+ " if ($owned) {",
64
+ " $result += @{ name = $name; modal = $true }",
65
+ " }",
66
+ "}",
67
+ "$result | ConvertTo-Json -Compress",
68
+ ].join("\n");
69
+ const r = await runCommand("powershell", ["-NoProfile", "-Command", script], opts.cwd, undefined, undefined, 8000);
70
+ if (r.exit_code !== 0) {
71
+ return {
72
+ is_modal_present: false,
73
+ target_type: "desktop",
74
+ detection_error: r.stderr.slice(0, 200),
75
+ };
76
+ }
77
+ try {
78
+ const parsed = r.stdout.trim() ? JSON.parse(r.stdout) : null;
79
+ const arr = Array.isArray(parsed) ? parsed : parsed ? [parsed] : [];
80
+ const modal = arr.find((w) => w.modal);
81
+ if (modal) {
82
+ return {
83
+ is_modal_present: true,
84
+ modal_description: `Windows modal: ${modal.name ?? "(unnamed)"}`,
85
+ suggested_action: "inspect",
86
+ confidence: 0.85,
87
+ target_type: "desktop",
88
+ };
89
+ }
90
+ }
91
+ catch {
92
+ /* fall through */
93
+ }
94
+ return { is_modal_present: false, target_type: "desktop" };
95
+ }
96
+ async function detectMacModal(opts) {
97
+ const app = opts.app_name ?? "frontmost";
98
+ const script = app === "frontmost"
99
+ ? `tell application "System Events" to get name of every window of (first process whose frontmost is true) whose subrole is "AXDialog"`
100
+ : `tell application "System Events" to get name of every window of process "${app}" whose subrole is "AXDialog"`;
101
+ const r = await runCommand("osascript", ["-e", script], opts.cwd, undefined, undefined, 8000);
102
+ if (r.exit_code !== 0) {
103
+ return {
104
+ is_modal_present: false,
105
+ target_type: "desktop",
106
+ detection_error: r.stderr.slice(0, 200),
107
+ };
108
+ }
109
+ const out = r.stdout.trim();
110
+ if (!out || out === "{}" || out === "missing value") {
111
+ return { is_modal_present: false, target_type: "desktop" };
112
+ }
113
+ return {
114
+ is_modal_present: true,
115
+ modal_description: `macOS modal: ${out}`,
116
+ suggested_action: "inspect",
117
+ confidence: 0.8,
118
+ target_type: "desktop",
119
+ };
120
+ }
121
+ async function detectLinuxModal(opts) {
122
+ // xdotool: search for windows tagged as dialog by EWMH.
123
+ const r = await runCommand("xdotool", ["search", "--onlyvisible", "--name", "."], opts.cwd, undefined, undefined, 8000);
124
+ if (r.exit_code !== 0) {
125
+ return {
126
+ is_modal_present: false,
127
+ target_type: "desktop",
128
+ detection_error: "xdotool unavailable; install xdotool via apt/dnf to enable Linux modal detection.",
129
+ };
130
+ }
131
+ // Best-effort. We don't pursue full xprop parsing here because the
132
+ // dominant Linux DE-set already surfaces modals via WM_HINTS
133
+ // transient_for; agents that need precision can call
134
+ // codeloop_handle_modal directly.
135
+ return { is_modal_present: false, target_type: "desktop" };
136
+ }
137
+ /**
138
+ * Heuristic for whether a given action is "modal-related" — used by
139
+ * layer 2 (pre-interact block) to decide whether an unrelated action
140
+ * should be refused while a modal is open.
141
+ */
142
+ export function isModalRelatedAction(action) {
143
+ const modalActions = new Set([
144
+ "click",
145
+ "double_click",
146
+ "right_click",
147
+ "hover",
148
+ "type",
149
+ "type_and_submit",
150
+ "type_and_tab",
151
+ "keystroke",
152
+ "hotkey",
153
+ "fill_form",
154
+ "select_option",
155
+ "toggle",
156
+ "wait",
157
+ ]);
158
+ return modalActions.has(action);
159
+ }
160
+ //# sourceMappingURL=modal_detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modal_detector.js","sourceRoot":"","sources":["../../src/runners/modal_detector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { RunnerResult } from "./base.js";
2
+ /**
3
+ * 0.1.51 H5 — Python verify runner.
4
+ *
5
+ * Detects the project's Python test framework in this priority order:
6
+ * 1. `pytest` if `pytest.ini`, `pyproject.toml [tool.pytest.ini_options]`,
7
+ * `setup.cfg [tool:pytest]`, or `tox.ini [pytest]` is present, OR
8
+ * a `conftest.py` exists, OR `tests/` / `test/` directory exists
9
+ * with `test_*.py` files.
10
+ * 2. Django `manage.py test` if `manage.py` is present.
11
+ * 3. `python -m unittest discover` as a last-resort fallback.
12
+ *
13
+ * Cross-platform: pure CLI invocations work identically on macOS,
14
+ * Linux, Windows. Picks `python3` first then `python` (the Windows
15
+ * launcher convention), then `py` as a final Windows-only fallback.
16
+ */
17
+ export declare function runPythonTests(cwd: string, logPath: string): Promise<RunnerResult>;
18
+ type PythonFramework = "pytest" | "django" | "unittest";
19
+ export declare function detectPythonTestFramework(cwd: string): PythonFramework | null;
20
+ export declare function parsePythonOutput(output: string, framework: PythonFramework): {
21
+ passed: number;
22
+ failed: number;
23
+ skipped: number;
24
+ };
25
+ export {};
26
+ //# sourceMappingURL=python_tests.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"python_tests.d.ts","sourceRoot":"","sources":["../../src/runners/python_tests.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAI9C;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,CAmEvB;AAED,KAAK,eAAe,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAAC;AAExD,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CA6D7E;AAOD,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,eAAe,GACzB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAwCrD"}
@@ -0,0 +1,181 @@
1
+ import { runCommand, makeSkippedResult, checkToolAvailable } from "./base.js";
2
+ import { existsSync, readFileSync, readdirSync } from "fs";
3
+ import { join } from "path";
4
+ /**
5
+ * 0.1.51 H5 — Python verify runner.
6
+ *
7
+ * Detects the project's Python test framework in this priority order:
8
+ * 1. `pytest` if `pytest.ini`, `pyproject.toml [tool.pytest.ini_options]`,
9
+ * `setup.cfg [tool:pytest]`, or `tox.ini [pytest]` is present, OR
10
+ * a `conftest.py` exists, OR `tests/` / `test/` directory exists
11
+ * with `test_*.py` files.
12
+ * 2. Django `manage.py test` if `manage.py` is present.
13
+ * 3. `python -m unittest discover` as a last-resort fallback.
14
+ *
15
+ * Cross-platform: pure CLI invocations work identically on macOS,
16
+ * Linux, Windows. Picks `python3` first then `python` (the Windows
17
+ * launcher convention), then `py` as a final Windows-only fallback.
18
+ */
19
+ export async function runPythonTests(cwd, logPath) {
20
+ const framework = detectPythonTestFramework(cwd);
21
+ if (framework === null) {
22
+ return makeSkippedResult("python_tests", "No Python test signals found (no pytest config, manage.py, conftest.py, or tests/ directory)");
23
+ }
24
+ // Pick the python interpreter. Order matters: prefer python3 on
25
+ // POSIX (where `python` is sometimes Python 2), then python (the
26
+ // Windows convention via the py launcher), then `py` as a final
27
+ // Windows fallback.
28
+ const candidates = process.platform === "win32"
29
+ ? ["python", "py", "python3"]
30
+ : ["python3", "python"];
31
+ let python = null;
32
+ for (const candidate of candidates) {
33
+ if (await checkToolAvailable(candidate)) {
34
+ python = candidate;
35
+ break;
36
+ }
37
+ }
38
+ if (python === null) {
39
+ return makeSkippedResult("python_tests", "Python interpreter not found on PATH (tried: " + candidates.join(", ") + ")");
40
+ }
41
+ let cmd;
42
+ let args;
43
+ if (framework === "pytest") {
44
+ // Use `python -m pytest` so we don't depend on a `pytest`
45
+ // executable being on PATH (it sometimes isn't, e.g. when the
46
+ // project uses a virtualenv that hasn't been `activate`d).
47
+ cmd = python;
48
+ args = ["-m", "pytest", "--tb=short", "-q"];
49
+ }
50
+ else if (framework === "django") {
51
+ cmd = python;
52
+ args = ["manage.py", "test", "--noinput"];
53
+ }
54
+ else {
55
+ cmd = python;
56
+ args = ["-m", "unittest", "discover"];
57
+ }
58
+ const start = Date.now();
59
+ const result = await runCommand(cmd, args, cwd, logPath);
60
+ const duration_ms = Date.now() - start;
61
+ const { passed, failed, skipped } = parsePythonOutput(result.stdout + "\n" + result.stderr, framework);
62
+ return {
63
+ runner_name: `python_${framework}`,
64
+ available: true,
65
+ exit_code: result.exit_code,
66
+ passed,
67
+ failed,
68
+ skipped,
69
+ log_path: logPath,
70
+ duration_ms,
71
+ stdout: result.stdout,
72
+ stderr: result.stderr,
73
+ };
74
+ }
75
+ export function detectPythonTestFramework(cwd) {
76
+ // Check for pytest signals first — this is the most common modern
77
+ // Python test setup and the most flexible (covers both unittest-
78
+ // style classes and pytest-native function-based tests).
79
+ if (existsSync(join(cwd, "pytest.ini")))
80
+ return "pytest";
81
+ if (existsSync(join(cwd, "pyproject.toml"))) {
82
+ try {
83
+ const pyproject = readFileSync(join(cwd, "pyproject.toml"), "utf-8");
84
+ if (/\[tool\.pytest\.ini_options\]/.test(pyproject))
85
+ return "pytest";
86
+ }
87
+ catch { /* fall through */ }
88
+ }
89
+ if (existsSync(join(cwd, "setup.cfg"))) {
90
+ try {
91
+ const setupCfg = readFileSync(join(cwd, "setup.cfg"), "utf-8");
92
+ if (/\[tool:pytest\]/.test(setupCfg))
93
+ return "pytest";
94
+ }
95
+ catch { /* fall through */ }
96
+ }
97
+ if (existsSync(join(cwd, "tox.ini"))) {
98
+ try {
99
+ const toxIni = readFileSync(join(cwd, "tox.ini"), "utf-8");
100
+ if (/\[pytest\]/.test(toxIni))
101
+ return "pytest";
102
+ }
103
+ catch { /* fall through */ }
104
+ }
105
+ // conftest.py at the root is a strong signal even without an
106
+ // explicit ini section — pytest auto-discovers from any conftest.
107
+ if (existsSync(join(cwd, "conftest.py")))
108
+ return "pytest";
109
+ // Look for a tests/ or test/ directory containing pytest-style
110
+ // test files. `test_*.py` is the pytest convention; pytest will
111
+ // discover them automatically once invoked.
112
+ for (const candidate of ["tests", "test"]) {
113
+ const testDir = join(cwd, candidate);
114
+ if (!existsSync(testDir))
115
+ continue;
116
+ try {
117
+ const files = readdirSync(testDir);
118
+ if (files.some((f) => /^test_.*\.py$/.test(f) || /_test\.py$/.test(f))) {
119
+ return "pytest";
120
+ }
121
+ }
122
+ catch { /* keep searching */ }
123
+ }
124
+ // Django: manage.py + a configured project. Use `manage.py test`.
125
+ if (existsSync(join(cwd, "manage.py")))
126
+ return "django";
127
+ // Anything-Python at all without a pytest config falls back to
128
+ // unittest discover (works against any subclass of TestCase).
129
+ if (existsSync(join(cwd, "setup.py")) ||
130
+ existsSync(join(cwd, "pyproject.toml")) ||
131
+ existsSync(join(cwd, "requirements.txt")) ||
132
+ existsSync(join(cwd, "Pipfile")) ||
133
+ existsSync(join(cwd, "poetry.lock"))) {
134
+ return "unittest";
135
+ }
136
+ return null;
137
+ }
138
+ function stripAnsi(str) {
139
+ // eslint-disable-next-line no-control-regex
140
+ return str.replace(/\x1B\[[0-9;]*[A-Za-z]/g, "");
141
+ }
142
+ export function parsePythonOutput(output, framework) {
143
+ const clean = stripAnsi(output);
144
+ if (framework === "pytest") {
145
+ // pytest summary line examples:
146
+ // "5 passed in 0.12s"
147
+ // "1 failed, 4 passed, 2 skipped in 0.34s"
148
+ // "===== 12 passed, 3 skipped, 1 deselected in 0.5s ====="
149
+ // Match each token independently against the summary section so
150
+ // the order doesn't matter.
151
+ const passedM = clean.match(/(\d+)\s+passed/);
152
+ const failedM = clean.match(/(\d+)\s+failed/);
153
+ const errorM = clean.match(/(\d+)\s+error(?:s|ed)?/);
154
+ const skippedM = clean.match(/(\d+)\s+skipped/);
155
+ return {
156
+ passed: passedM ? parseInt(passedM[1], 10) : 0,
157
+ failed: (failedM ? parseInt(failedM[1], 10) : 0) + (errorM ? parseInt(errorM[1], 10) : 0),
158
+ skipped: skippedM ? parseInt(skippedM[1], 10) : 0,
159
+ };
160
+ }
161
+ // Django test runner and unittest discover both end with:
162
+ // "Ran N tests in 0.012s"
163
+ // "OK" (success)
164
+ // "OK (skipped=2)"
165
+ // "FAILED (failures=1, errors=2, skipped=3)"
166
+ const ranM = clean.match(/Ran\s+(\d+)\s+tests?/);
167
+ const ran = ranM ? parseInt(ranM[1], 10) : 0;
168
+ const failuresM = clean.match(/failures\s*=\s*(\d+)/);
169
+ const errorsM = clean.match(/errors\s*=\s*(\d+)/);
170
+ const skippedM = clean.match(/skipped\s*=\s*(\d+)/);
171
+ const failures = failuresM ? parseInt(failuresM[1], 10) : 0;
172
+ const errors = errorsM ? parseInt(errorsM[1], 10) : 0;
173
+ const skipped = skippedM ? parseInt(skippedM[1], 10) : 0;
174
+ // Whether the run ended with OK is the source of truth for pass/fail.
175
+ // If we matched neither failures= nor errors= and saw "OK", everything
176
+ // that ran (minus skipped) is a pass.
177
+ const failed = failures + errors;
178
+ const passed = Math.max(0, ran - failed - skipped);
179
+ return { passed, failed, skipped };
180
+ }
181
+ //# sourceMappingURL=python_tests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"python_tests.js","sourceRoot":"","sources":["../../src/runners/python_tests.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { RunnerResult } from "./base.js";
2
+ /**
3
+ * 0.1.51 H5 — Rust verify runner.
4
+ *
5
+ * Detects via `Cargo.toml` at the project root and runs:
6
+ * cargo test --no-fail-fast
7
+ *
8
+ * `--no-fail-fast` mirrors the existing Flutter / generic runners in
9
+ * this package (collect every failure in one pass; the agent then
10
+ * has all the data it needs for `codeloop_diagnose`).
11
+ *
12
+ * Cross-platform: cargo is the same CLI on macOS / Linux / Windows.
13
+ * If cargo isn't on PATH, the runner returns a skipped result with a
14
+ * specific install hint instead of throwing.
15
+ *
16
+ * Tauri detection note: Tauri projects have BOTH a top-level
17
+ * `package.json` AND a `src-tauri/Cargo.toml`. We honour both — when
18
+ * the top-level Cargo.toml is missing but `src-tauri/Cargo.toml`
19
+ * exists, we run cargo test inside `src-tauri/` so the Rust backend
20
+ * tests are exercised on Tauri verify.
21
+ */
22
+ export declare function runRustTests(cwd: string, logPath: string): Promise<RunnerResult>;
23
+ export declare function parseCargoOutput(output: string): {
24
+ passed: number;
25
+ failed: number;
26
+ skipped: number;
27
+ };
28
+ //# sourceMappingURL=rust_tests.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rust_tests.d.ts","sourceRoot":"","sources":["../../src/runners/rust_tests.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAI9C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,CA4CvB;AAOD,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,GACb;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAgBrD"}
@@ -0,0 +1,76 @@
1
+ import { runCommand, makeSkippedResult, checkToolAvailable } from "./base.js";
2
+ import { existsSync } from "fs";
3
+ import { join } from "path";
4
+ /**
5
+ * 0.1.51 H5 — Rust verify runner.
6
+ *
7
+ * Detects via `Cargo.toml` at the project root and runs:
8
+ * cargo test --no-fail-fast
9
+ *
10
+ * `--no-fail-fast` mirrors the existing Flutter / generic runners in
11
+ * this package (collect every failure in one pass; the agent then
12
+ * has all the data it needs for `codeloop_diagnose`).
13
+ *
14
+ * Cross-platform: cargo is the same CLI on macOS / Linux / Windows.
15
+ * If cargo isn't on PATH, the runner returns a skipped result with a
16
+ * specific install hint instead of throwing.
17
+ *
18
+ * Tauri detection note: Tauri projects have BOTH a top-level
19
+ * `package.json` AND a `src-tauri/Cargo.toml`. We honour both — when
20
+ * the top-level Cargo.toml is missing but `src-tauri/Cargo.toml`
21
+ * exists, we run cargo test inside `src-tauri/` so the Rust backend
22
+ * tests are exercised on Tauri verify.
23
+ */
24
+ export async function runRustTests(cwd, logPath) {
25
+ let cargoCwd = cwd;
26
+ if (!existsSync(join(cwd, "Cargo.toml"))) {
27
+ const tauriCargo = join(cwd, "src-tauri", "Cargo.toml");
28
+ if (existsSync(tauriCargo)) {
29
+ cargoCwd = join(cwd, "src-tauri");
30
+ }
31
+ else {
32
+ return makeSkippedResult("rust_tests", "No Cargo.toml at the project root or src-tauri/");
33
+ }
34
+ }
35
+ if (!(await checkToolAvailable("cargo"))) {
36
+ return makeSkippedResult("rust_tests", "cargo not found on PATH. Install via https://rustup.rs (POSIX/macOS) or `winget install Rustlang.Rustup` (Windows), then re-run codeloop_verify.");
37
+ }
38
+ const start = Date.now();
39
+ const result = await runCommand("cargo", ["test", "--no-fail-fast"], cargoCwd, logPath);
40
+ const duration_ms = Date.now() - start;
41
+ const { passed, failed, skipped } = parseCargoOutput(result.stdout + "\n" + result.stderr);
42
+ return {
43
+ runner_name: "rust_tests",
44
+ available: true,
45
+ exit_code: result.exit_code,
46
+ passed,
47
+ failed,
48
+ skipped,
49
+ log_path: logPath,
50
+ duration_ms,
51
+ stdout: result.stdout,
52
+ stderr: result.stderr,
53
+ };
54
+ }
55
+ function stripAnsi(str) {
56
+ // eslint-disable-next-line no-control-regex
57
+ return str.replace(/\x1B\[[0-9;]*[A-Za-z]/g, "");
58
+ }
59
+ export function parseCargoOutput(output) {
60
+ const clean = stripAnsi(output);
61
+ // cargo test prints one summary line per test target:
62
+ // "test result: ok. 12 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.05s"
63
+ // "test result: FAILED. 8 passed; 4 failed; 0 ignored; 0 measured"
64
+ // A workspace with N crates emits N such lines, so sum across them.
65
+ const summaryRe = /test result:\s+(?:ok|FAILED)\.\s+(\d+)\s+passed;\s+(\d+)\s+failed;\s+(\d+)\s+ignored/g;
66
+ let passed = 0;
67
+ let failed = 0;
68
+ let skipped = 0;
69
+ for (const m of clean.matchAll(summaryRe)) {
70
+ passed += parseInt(m[1], 10);
71
+ failed += parseInt(m[2], 10);
72
+ skipped += parseInt(m[3], 10);
73
+ }
74
+ return { passed, failed, skipped };
75
+ }
76
+ //# sourceMappingURL=rust_tests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rust_tests.js","sourceRoot":"","sources":["../../src/runners/rust_tests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE9E,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAAW,EACX,OAAe;IAEf,IAAI,QAAQ,GAAG,GAAG,CAAC;IACnB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC;QACzC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;QACxD,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC3B,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,OAAO,iBAAiB,CACtB,YAAY,EACZ,iDAAiD,CAClD,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,CAAC,CAAC,MAAM,kBAAkB,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QACzC,OAAO,iBAAiB,CACtB,YAAY,EACZ,kJAAkJ,CACnJ,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,MAAM,MAAM,GAAG,MAAM,UAAU,CAC7B,OAAO,EACP,CAAC,MAAM,EAAE,gBAAgB,CAAC,EAC1B,QAAQ,EACR,OAAO,CACR,CAAC;IACF,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;IAEvC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,gBAAgB,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAE3F,OAAO;QACL,WAAW,EAAE,YAAY;QACzB,SAAS,EAAE,IAAI;QACf,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,MAAM;QACN,MAAM;QACN,OAAO;QACP,QAAQ,EAAE,OAAO;QACjB,WAAW;QACX,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,MAAM,EAAE,MAAM,CAAC,MAAM;KACtB,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,GAAW;IAC5B,4CAA4C;IAC5C,OAAO,GAAG,CAAC,OAAO,CAAC,wBAAwB,EAAE,EAAE,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,MAAc;IAEd,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IAChC,sDAAsD;IACtD,qGAAqG;IACrG,qEAAqE;IACrE,oEAAoE;IACpE,MAAM,SAAS,GAAG,uFAAuF,CAAC;IAC1G,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7B,MAAM,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7B,OAAO,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;AACrC,CAAC"}