nativeapptemplate-agent 0.1.2 → 0.2.1

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 (108) hide show
  1. package/CLAUDE.md +10 -8
  2. package/README.md +65 -21
  3. package/dist/agents/judge.d.ts +8 -0
  4. package/dist/agents/judge.d.ts.map +1 -1
  5. package/dist/agents/judge.js +167 -29
  6. package/dist/agents/judge.js.map +1 -1
  7. package/dist/agents/repair.d.ts +14 -0
  8. package/dist/agents/repair.d.ts.map +1 -0
  9. package/dist/agents/repair.js +103 -0
  10. package/dist/agents/repair.js.map +1 -0
  11. package/dist/agents/types.d.ts +34 -1
  12. package/dist/agents/types.d.ts.map +1 -1
  13. package/dist/dispatch.d.ts +21 -2
  14. package/dist/dispatch.d.ts.map +1 -1
  15. package/dist/dispatch.js +250 -14
  16. package/dist/dispatch.js.map +1 -1
  17. package/dist/env-bridge.d.ts +17 -0
  18. package/dist/env-bridge.d.ts.map +1 -0
  19. package/dist/env-bridge.js +211 -0
  20. package/dist/env-bridge.js.map +1 -0
  21. package/dist/index.d.ts +11 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +88 -3
  24. package/dist/index.js.map +1 -1
  25. package/dist/mcp.d.ts +5 -0
  26. package/dist/mcp.d.ts.map +1 -0
  27. package/dist/mcp.js +83 -0
  28. package/dist/mcp.js.map +1 -0
  29. package/dist/mobile.d.ts +31 -0
  30. package/dist/mobile.d.ts.map +1 -0
  31. package/dist/mobile.js +179 -0
  32. package/dist/mobile.js.map +1 -0
  33. package/dist/rails-lifecycle.d.ts +16 -0
  34. package/dist/rails-lifecycle.d.ts.map +1 -0
  35. package/dist/rails-lifecycle.js +184 -0
  36. package/dist/rails-lifecycle.js.map +1 -0
  37. package/dist/rename-overrides.d.ts +23 -0
  38. package/dist/rename-overrides.d.ts.map +1 -0
  39. package/dist/rename-overrides.js +58 -0
  40. package/dist/rename-overrides.js.map +1 -0
  41. package/dist/repair-loop.d.ts +25 -0
  42. package/dist/repair-loop.d.ts.map +1 -0
  43. package/dist/repair-loop.js +67 -0
  44. package/dist/repair-loop.js.map +1 -0
  45. package/dist/report/collect.d.ts +28 -0
  46. package/dist/report/collect.d.ts.map +1 -0
  47. package/dist/report/collect.js +117 -0
  48. package/dist/report/collect.js.map +1 -0
  49. package/dist/report/model.d.ts +49 -0
  50. package/dist/report/model.d.ts.map +1 -0
  51. package/dist/report/model.js +2 -0
  52. package/dist/report/model.js.map +1 -0
  53. package/dist/report/render.d.ts +3 -0
  54. package/dist/report/render.d.ts.map +1 -0
  55. package/dist/report/render.js +242 -0
  56. package/dist/report/render.js.map +1 -0
  57. package/dist/report/theme.d.ts +2 -0
  58. package/dist/report/theme.d.ts.map +1 -0
  59. package/dist/report/theme.js +115 -0
  60. package/dist/report/theme.js.map +1 -0
  61. package/dist/slug.d.ts +3 -0
  62. package/dist/slug.d.ts.map +1 -1
  63. package/dist/slug.js +30 -0
  64. package/dist/slug.js.map +1 -1
  65. package/dist/validation/capture.d.ts.map +1 -1
  66. package/dist/validation/capture.js +19 -2
  67. package/dist/validation/capture.js.map +1 -1
  68. package/dist/validation/discover.js +1 -1
  69. package/dist/validation/discover.js.map +1 -1
  70. package/dist/validation/index.d.ts +8 -2
  71. package/dist/validation/index.d.ts.map +1 -1
  72. package/dist/validation/index.js +4 -1
  73. package/dist/validation/index.js.map +1 -1
  74. package/dist/validation/launch.d.ts +10 -0
  75. package/dist/validation/launch.d.ts.map +1 -1
  76. package/dist/validation/launch.js +127 -12
  77. package/dist/validation/launch.js.map +1 -1
  78. package/dist/validation/layer1.d.ts.map +1 -1
  79. package/dist/validation/layer1.js +12 -4
  80. package/dist/validation/layer1.js.map +1 -1
  81. package/dist/validation/layer2.js +1 -1
  82. package/dist/validation/layer2.js.map +1 -1
  83. package/dist/validation/scenarios/queue.d.ts +12 -0
  84. package/dist/validation/scenarios/queue.d.ts.map +1 -0
  85. package/dist/validation/scenarios/queue.js +216 -0
  86. package/dist/validation/scenarios/queue.js.map +1 -0
  87. package/dist/validation/stage1.d.ts +2 -0
  88. package/dist/validation/stage1.d.ts.map +1 -1
  89. package/dist/validation/stage1.js +2 -0
  90. package/dist/validation/stage1.js.map +1 -1
  91. package/dist/validation/stage2-judge.d.ts +23 -0
  92. package/dist/validation/stage2-judge.d.ts.map +1 -0
  93. package/dist/validation/stage2-judge.js +225 -0
  94. package/dist/validation/stage2-judge.js.map +1 -0
  95. package/dist/validation/stage2.d.ts +74 -0
  96. package/dist/validation/stage2.d.ts.map +1 -0
  97. package/dist/validation/stage2.js +328 -0
  98. package/dist/validation/stage2.js.map +1 -0
  99. package/dist/validation/visual-judge.d.ts +37 -0
  100. package/dist/validation/visual-judge.d.ts.map +1 -1
  101. package/dist/validation/visual-judge.js +100 -16
  102. package/dist/validation/visual-judge.js.map +1 -1
  103. package/dist/version.d.ts +2 -0
  104. package/dist/version.d.ts.map +1 -0
  105. package/dist/version.js +17 -0
  106. package/dist/version.js.map +1 -0
  107. package/package.json +6 -3
  108. package/scripts/ruby/rename.rb +7 -3
@@ -0,0 +1,216 @@
1
+ export function buildQueueScenario(domain, inputs, platform = "ios") {
2
+ const queueEntryName = renamedTo(domain, "ItemTag") ?? "ItemTag";
3
+ // Renamed Shopkeeper class (Vet for clinic, Host for restaurant,
4
+ // Curator for journal, etc.). Used by the rails_runner confirm
5
+ // step to bypass email confirmation server-side.
6
+ const shopkeeperClass = renamedTo(domain, "Shopkeeper") ?? "Shopkeeper";
7
+ // Sign In form-fill diverges per platform: iOS Compose uses a
8
+ // SecureTextField type for the password (queryable by tap_field);
9
+ // Android Compose's empty OutlinedTextField doesn't surface an
10
+ // EditText in the accessibility tree until it has content. Tap the
11
+ // visible TextView label to focus the field instead.
12
+ const signInFormFill = platform === "ios"
13
+ ? [
14
+ { kind: "tap_field", fieldTypes: ["TextField", "EditText"], nth: 0 },
15
+ { kind: "type", text: inputs.email, submit: true },
16
+ { kind: "tap_field", fieldTypes: ["SecureTextField", "EditText"], nth: -1 },
17
+ { kind: "type", text: inputs.password, submit: true },
18
+ ]
19
+ : [
20
+ // exact:true scopes to the standalone label TextView. Without
21
+ // it, "Password" substring-matches "Forgot your password?" (a
22
+ // link that navigates away to the password-reset screen).
23
+ //
24
+ // submit:true is OMITTED on Android because mobile-mcp injects
25
+ // a literal newline character ("\n", surfaces as "
") into
26
+ // the field instead of dismissing the keyboard — which then
27
+ // fails Email validation. Use press_button "BACK" instead to
28
+ // dismiss the keyboard before the next tap.
29
+ { kind: "tap_text", text: "Email", exact: true },
30
+ { kind: "type", text: inputs.email },
31
+ { kind: "press_button", button: "BACK", optional: true },
32
+ { kind: "tap_text", text: "Password", exact: true },
33
+ { kind: "type", text: inputs.password },
34
+ { kind: "press_button", button: "BACK", optional: true },
35
+ ];
36
+ const steps = [
37
+ // ---- Verified: Welcome → Auth choice → Sign Up form ----
38
+ { kind: "wait_for_text", text: "Welcome to" },
39
+ { kind: "screenshot", label: "01-welcome" },
40
+ { kind: "tap_text", text: "Start" },
41
+ { kind: "wait_for_text", text: "Sign Up for an Account" },
42
+ { kind: "screenshot", label: "02-auth-choice" },
43
+ { kind: "tap_text", text: "Sign Up for an Account" },
44
+ { kind: "wait_for_text", text: "Full Name" },
45
+ { kind: "screenshot", label: "03-signup-form" },
46
+ // Form fields don't bind to their labels — tap_text on the label
47
+ // hits the dead StaticText and leaves the TextField unfocused.
48
+ // tap_below_text bias-taps where the field actually sits.
49
+ { kind: "tap_below_text", text: "Full Name" },
50
+ { kind: "type", text: inputs.fullName },
51
+ { kind: "tap_below_text", text: "Email" },
52
+ { kind: "type", text: inputs.email },
53
+ { kind: "tap_below_text", text: "Password" },
54
+ // submit:true dismisses the on-screen keyboard after the last
55
+ // field — without it, the keyboard occludes the bottom Sign Up
56
+ // button and tap_text "Sign Up" hits the keyboard area instead.
57
+ { kind: "type", text: inputs.password, submit: true },
58
+ // Dismiss the on-screen keyboard before tapping Sign Up. On
59
+ // iOS the keychain dialog handler below catches things; on
60
+ // Android the Sign Up submit button (Compose, rendered as
61
+ // TextView at y~2613) sits near the bottom and the keyboard can
62
+ // occlude it / push it off-tappable area. press_button "BACK" is
63
+ // Android's keyboard-dismiss; on iOS it's not a real key and
64
+ // mobile-mcp may error or no-op — we wrap in try/catch via the
65
+ // press_button no-throw semantics in the runner.
66
+ { kind: "press_button", button: "BACK", optional: true },
67
+ // tap_text prefers Button-typed matches over StaticText/TextView,
68
+ // so this hits the form's bottom Sign Up button rather than the
69
+ // page-header that shares the label. On Android Compose, the
70
+ // bottom-most fallback (when no Button-type match exists) finds
71
+ // the lower TextView "Sign Up".
72
+ { kind: "tap_text", text: "Sign Up" },
73
+ // After successful signup, iOS shows the Keychain "Save password?"
74
+ // system dialog. optional:true means we no-op if it doesn't appear
75
+ // (e.g. on Android, or if Keychain is disabled). Try the Japanese
76
+ // and English labels — Japanese-localized sims see "今はしない",
77
+ // English see "Not Now". Same dialog, locale-dependent label.
78
+ { kind: "tap_text", text: "今はしない", optional: true, timeoutMs: 3_000 },
79
+ { kind: "tap_text", text: "Not Now", optional: true, timeoutMs: 3_000 },
80
+ // Substrate uses devise_token_auth's email-confirmation flow:
81
+ // signup → "check your email" banner → user must click the email
82
+ // link before they can sign in. We can't click the email
83
+ // programmatically via UI alone, so bypass server-side: confirm
84
+ // the just-created Vet directly. devise's `.confirm` flips
85
+ // confirmed_at and unblocks sign-in.
86
+ {
87
+ kind: "rails_runner",
88
+ outDir: inputs.railsOutDir,
89
+ ruby: `${shopkeeperClass}.find_by(email: ${JSON.stringify(inputs.email)})&.confirm`,
90
+ label: "confirm vet",
91
+ },
92
+ // After confirmation, dismiss the post-signup banner (if visible)
93
+ // and tap Sign In to land on the (renamed primary noun) list. iOS
94
+ // labels the banner button "Close"; Android Compose substrate
95
+ // labels it "Dismiss". Both are optional — only one fires per
96
+ // platform; the other no-ops.
97
+ { kind: "tap_text", text: "Close", optional: true, timeoutMs: 3_000 },
98
+ { kind: "tap_text", text: "Dismiss", optional: true, timeoutMs: 3_000 },
99
+ // The iOS Keychain "Save password?" dialog can surface LATE — after the
100
+ // rails_runner confirm above, not right at signup — so it re-covers the
101
+ // auth screen here and intercepts the Sign-In tap (observed: poll saw only
102
+ // the 5 dialog elements). Dismiss it again immediately before tapping Sign
103
+ // In; optional, so it no-ops on Android / when already gone.
104
+ { kind: "tap_text", text: "今はしない", optional: true, timeoutMs: 3_000 },
105
+ { kind: "tap_text", text: "Not Now", optional: true, timeoutMs: 3_000 },
106
+ { kind: "tap_text", text: "Sign In to Your Account" },
107
+ { kind: "wait_for_text", text: "Email", timeoutMs: 10_000 },
108
+ // Sign In form-fill is platform-specific (built above). iOS uses
109
+ // tap_field with SecureTextField; Android uses tap_text "Email" /
110
+ // "Password" since Compose's empty OutlinedTextField doesn't
111
+ // surface an EditText in the accessibility tree.
112
+ ...signInFormFill,
113
+ { kind: "tap_text", text: "Sign In" },
114
+ // iOS Keychain shows "Save password?" again after a successful
115
+ // Sign In (not just Sign Up). Same dismissal pattern.
116
+ { kind: "tap_text", text: "今はしない", optional: true, timeoutMs: 3_000 },
117
+ { kind: "tap_text", text: "Not Now", optional: true, timeoutMs: 3_000 },
118
+ // PAID-substrate-only intro modal after Sign In: "You are in
119
+ // personal organization. ... Switch to or create an organization
120
+ // to share..." with a "Create <Primary>" CTA that opens the
121
+ // creation form. Free edition has no multi-tenancy so the modal
122
+ // never appears — these waits/taps are effectively skipped on
123
+ // free because the post-Sign-In screen ALREADY shows
124
+ // primaryName (free) without a modal in the way.
125
+ //
126
+ // Free path: wait_for_text "Clinic" matches the list view
127
+ // header; the optional Create-* taps below find nothing and
128
+ // no-op (but the unconditional `tap_text "Add"` later matches
129
+ // the free toolbar).
130
+ //
131
+ // Paid path: wait_for_text "Clinic" matches the modal's Create
132
+ // <Primary> button label (substring). We can't tap it yet
133
+ // because the modal might still be animating in — explicit
134
+ // wait_for_text below with a longer timeout + exact:true on the
135
+ // "Create <Primary>" button gates correctly.
136
+ //
137
+ // Paid-substrate-only post-Sign-In multi-tenancy explainer.
138
+ // Per platform:
139
+ // iOS: two modal phases — v1 "OK"+"Go to Organizations",
140
+ // then v2 "Create <Primary>"+"Go to Organizations"+
141
+ // "Cancel". OK then Cancel both dismiss.
142
+ // Android: single snackbar with a "Dismiss" button.
143
+ // All optional with short timeouts so free no-ops cleanly. The
144
+ // walk picks up at the auto-seeded "Sample <Primary>" the
145
+ // substrate created during signup — no need to navigate the
146
+ // create-resource flow at all.
147
+ { kind: "tap_text", text: "OK", exact: true, optional: true, timeoutMs: 5_000 },
148
+ { kind: "tap_text", text: "Cancel", exact: true, optional: true, timeoutMs: 5_000 },
149
+ { kind: "tap_text", text: "Dismiss", exact: true, optional: true, timeoutMs: 5_000 },
150
+ // ---- Unverified below — best-effort guesses, expect drift ----
151
+ // After auth + modal dismissals, drill DIRECTLY into the
152
+ // substrate's auto-seeded "Sample <Primary>" resource (created
153
+ // by Account#create_default_clinic! on every signup, both
154
+ // editions). This skips the per-edition Create flow complexity
155
+ // (paid hides Create behind multi-tenancy modals; free shows
156
+ // an Add button) — the auto-seeded resource is enough to
157
+ // demonstrate the rubric (renamed domain content with visible
158
+ // state badge).
159
+ //
160
+ // iOS requests notification permission at runtime when the list first
161
+ // loads. A system alert makes the accessibility tree return ONLY the
162
+ // alert's elements, hiding the list — so wait_for_text "Sample" below sees
163
+ // just the dialog (observed: "5 elements") and times out. Dismiss it
164
+ // (Allow; JP + EN labels, exact so "許可" doesn't match "許可しない").
165
+ // Optional + Android no-op: Android pre-grants POST_NOTIFICATIONS (#78).
166
+ { kind: "tap_text", text: "許可", exact: true, optional: true, timeoutMs: 5_000 },
167
+ { kind: "tap_text", text: "Allow", exact: true, optional: true, timeoutMs: 3_000 },
168
+ // The iOS Keychain "Save password?" dialog also re-appears here — after a
169
+ // successful Sign In, once the list loads — covering "Sample Household"
170
+ // (observed: poll saw only the 5 dialog elements). Dismiss it again right
171
+ // before the Sample wait. Optional; Android no-op.
172
+ { kind: "tap_text", text: "今はしない", optional: true, timeoutMs: 5_000 },
173
+ { kind: "tap_text", text: "Not Now", optional: true, timeoutMs: 3_000 },
174
+ // Paid-only "You are in personal organization" multi-tenancy modal ALSO
175
+ // surfaces here — late, once the list loads, covering "Sample Household".
176
+ // Tap OK (and Cancel for the 2nd phase) to dismiss to the list. Optional;
177
+ // free edition / Android no-op.
178
+ { kind: "tap_text", text: "OK", exact: true, optional: true, timeoutMs: 5_000 },
179
+ { kind: "tap_text", text: "Cancel", exact: true, optional: true, timeoutMs: 3_000 },
180
+ // The substrate's seed name "Sample" doesn't get renamed (it's
181
+ // a value, not a domain identifier). On every edition the list
182
+ // shows an entry containing "Sample" — wait for it.
183
+ { kind: "wait_for_text", text: "Sample", timeoutMs: 15_000 },
184
+ { kind: "screenshot", label: "04-primary-list-with-sample" },
185
+ // Drill into the sample resource to reach the queue-entry list.
186
+ // Substrate auto-creates a "Sample" queue entry inside the
187
+ // sample primary resource too (Clinic#create_sample_patient or
188
+ // equivalent), so the list isn't empty — this screenshot shows
189
+ // real domain content with the renamed state badge.
190
+ { kind: "tap_text", text: "Sample" },
191
+ { kind: "wait_for_text", text: queueEntryName, timeoutMs: 15_000 },
192
+ { kind: "screenshot", label: "06-queue-entry-list" },
193
+ // Stage 2 ends here. The agent has demonstrated end-to-end:
194
+ // planner → renamer → workers → reviewer → Layer 1 + 2 + 3
195
+ // Stage 1 → bin/dev → Stage 2 walk through signup → email-
196
+ // confirm bypass → sign in → resource creation → drill into
197
+ // queue-entry list with the substrate's auto-seeded sample.
198
+ //
199
+ // Adding a NEW entry + toggling its state are deferred — they
200
+ // require mapping the substrate's "+" / swipe / state-toggle
201
+ // affordances which are icon-only and aren't reliably matched
202
+ // by tap_text. Layer 3 judges the screenshot above, which
203
+ // already contains the renamed primary resource ("Vet Clinic
204
+ // Queue"), the renamed queue entry noun ("Patient" via
205
+ // queueEntryName), and the renamed state badge — sufficient
206
+ // to satisfy the Stage 2 rubric.
207
+ ];
208
+ return {
209
+ name: `queue-crud-${domain.slug}`,
210
+ steps,
211
+ };
212
+ }
213
+ function renamedTo(domain, from) {
214
+ return domain.renamePlan.find((p) => p.from === from)?.to;
215
+ }
216
+ //# sourceMappingURL=queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.js","sourceRoot":"","sources":["../../../src/validation/scenarios/queue.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,8 @@ export type Stage1VisualInput = {
8
8
  rubric?: readonly Layer3Criterion[];
9
9
  renderWaitMs?: number;
10
10
  samplesPerCriterion?: number;
11
+ iosRecover?: () => Promise<void>;
12
+ androidRecover?: () => Promise<void>;
11
13
  };
12
14
  export type Stage1VisualResult = {
13
15
  ios?: VisualJudgeResult;
@@ -1 +1 @@
1
- {"version":3,"file":"stage1.d.ts","sourceRoot":"","sources":["../../src/validation/stage1.ts"],"names":[],"mappings":"AAGA,OAAO,EAAyC,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAClG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,MAAM,MAAM,iBAAiB,GAAG;IAE9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,CAAC,EAAE,iBAAiB,CAAC;IACxB,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC7B,CAAC;AAaF,wBAAsB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA4C3F"}
1
+ {"version":3,"file":"stage1.d.ts","sourceRoot":"","sources":["../../src/validation/stage1.ts"],"names":[],"mappings":"AAGA,OAAO,EAAyC,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAClG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,MAAM,MAAM,iBAAiB,GAAG;IAE9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAI7B,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,CAAC,EAAE,iBAAiB,CAAC;IACxB,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC7B,CAAC;AAaF,wBAAsB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA8C3F"}
@@ -30,6 +30,7 @@ export async function runStage1Visual(input) {
30
30
  rubric,
31
31
  ...(input.renderWaitMs !== undefined ? { renderWaitMs: input.renderWaitMs } : {}),
32
32
  ...(input.samplesPerCriterion !== undefined ? { samplesPerCriterion: input.samplesPerCriterion } : {}),
33
+ ...(input.iosRecover !== undefined ? { recover: input.iosRecover } : {}),
33
34
  });
34
35
  }
35
36
  else {
@@ -48,6 +49,7 @@ export async function runStage1Visual(input) {
48
49
  rubric,
49
50
  ...(input.renderWaitMs !== undefined ? { renderWaitMs: input.renderWaitMs } : {}),
50
51
  ...(input.samplesPerCriterion !== undefined ? { samplesPerCriterion: input.samplesPerCriterion } : {}),
52
+ ...(input.androidRecover !== undefined ? { recover: input.androidRecover } : {}),
51
53
  });
52
54
  }
53
55
  else {
@@ -1 +1 @@
1
- {"version":3,"file":"stage1.js","sourceRoot":"","sources":["../../src/validation/stage1.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAA0B,MAAM,mBAAmB,CAAC;AAoBlG,0EAA0E;AAC1E,6EAA6E;AAC7E,6EAA6E;AAC7E,EAAE;AACF,wEAAwE;AACxE,6EAA6E;AAC7E,EAAE;AACF,2BAA2B;AAC3B,iEAAiE;AACjE,sEAAsE;AACtE,qEAAqE;AACrE,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,KAAwB;IAC5D,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC;IACvF,MAAM,KAAK,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,qBAAqB,CAAC;IAErD,MAAM,MAAM,GAAuB,EAAE,CAAC;IAEtC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjB,MAAM,GAAG,GAAG,MAAM,mBAAmB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACpD,IAAI,GAAG,EAAE,CAAC;YACR,MAAM,CAAC,GAAG,GAAG,MAAM,cAAc,CAAC;gBAChC,QAAQ,EAAE,KAAK;gBACf,YAAY,EAAE,GAAG,CAAC,OAAO;gBACzB,QAAQ,EAAE,GAAG,CAAC,QAAQ;gBACtB,cAAc,EAAE,IAAI,CAAC,aAAa,EAAE,cAAc,CAAC;gBACnD,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,MAAM;gBACN,GAAG,CAAC,KAAK,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjF,GAAG,CAAC,KAAK,CAAC,mBAAmB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,mBAAmB,EAAE,KAAK,CAAC,mBAAmB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACvG,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,GAAG,WAAW,CAAC,KAAK,EAAE,4DAA4D,CAAC,CAAC;QAChG,CAAC;IACH,CAAC;IAED,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACrB,MAAM,OAAO,GAAG,MAAM,uBAAuB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAChE,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,CAAC,OAAO,GAAG,MAAM,cAAc,CAAC;gBACpC,QAAQ,EAAE,SAAS;gBACnB,YAAY,EAAE,OAAO,CAAC,OAAO;gBAC7B,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,cAAc,EAAE,IAAI,CAAC,aAAa,EAAE,kBAAkB,CAAC;gBACvD,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,MAAM;gBACN,GAAG,CAAC,KAAK,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjF,GAAG,CAAC,KAAK,CAAC,mBAAmB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,mBAAmB,EAAE,KAAK,CAAC,mBAAmB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACvG,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,OAAO,GAAG,WAAW,CAAC,SAAS,EAAE,gEAAgE,CAAC,CAAC;QAC5G,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,WAAW,CAAC,QAA2B,EAAE,KAAa;IAC7D,OAAO;QACL,EAAE,EAAE,KAAK;QACT,MAAM,EAAE;YACN,EAAE,EAAE,KAAK;YACT,OAAO,EAAE,YAAY,QAAQ,WAAW;YACxC,UAAU,EAAE,CAAC;YACb,KAAK;SACN;QACD,KAAK;KACN,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"stage1.js","sourceRoot":"","sources":["../../src/validation/stage1.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { type Stage2Scenario } from "./stage2.js";
2
+ import { type Layer3Criterion } from "./layer3.js";
3
+ import { type MobileClient } from "../mobile.js";
4
+ import type { Stage2PlatformReport } from "../agents/types.js";
5
+ export type Stage2VisualInput = {
6
+ iosScenario?: Stage2Scenario;
7
+ androidScenario?: Stage2Scenario;
8
+ screenshotDir?: string;
9
+ spec: string;
10
+ rubric?: readonly Layer3Criterion[];
11
+ iosAppId?: string;
12
+ androidAppId?: string;
13
+ iosClient?: MobileClient;
14
+ androidClient?: MobileClient;
15
+ };
16
+ export type Stage2VisualResult = {
17
+ ios?: Stage2PlatformReport;
18
+ android?: Stage2PlatformReport;
19
+ };
20
+ export declare const DEFAULT_STAGE2_RUBRIC: readonly Layer3Criterion[];
21
+ export declare function runStage2Visual(input: Stage2VisualInput): Promise<Stage2VisualResult>;
22
+ export declare function selectDevice(client: MobileClient, platform: "ios" | "android"): Promise<string | undefined>;
23
+ //# sourceMappingURL=stage2-judge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stage2-judge.d.ts","sourceRoot":"","sources":["../../src/validation/stage2-judge.ts"],"names":[],"mappings":"AAGA,OAAO,EAAgE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAChH,OAAO,EAAa,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAsB,KAAK,YAAY,EAAsB,MAAM,cAAc,CAAC;AACzF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAsB/D,MAAM,MAAM,iBAAiB,GAAG;IAI9B,WAAW,CAAC,EAAE,cAAc,CAAC;IAC7B,eAAe,CAAC,EAAE,cAAc,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IAKpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB,aAAa,CAAC,EAAE,YAAY,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,CAAC,EAAE,oBAAoB,CAAC;IAC3B,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC,CAAC;AAMF,eAAO,MAAM,qBAAqB,EAAE,SAAS,eAAe,EAW3D,CAAC;AAEF,wBAAsB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA6B3F;AA2FD,wBAAsB,YAAY,CAChC,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,KAAK,GAAG,SAAS,GAC1B,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAoD7B"}
@@ -0,0 +1,225 @@
1
+ import { join } from "node:path";
2
+ import { mkdir } from "node:fs/promises";
3
+ import { setTimeout as sleep } from "node:timers/promises";
4
+ import { runStage2Scenario, recoverFromErrorScreen } from "./stage2.js";
5
+ import { runLayer3 } from "./layer3.js";
6
+ import { createMobileClient } from "../mobile.js";
7
+ // Stage 2 rubric — domain content (because we've now navigated to the
8
+ // list/detail view past auth) plus a defensive substrate-leak check.
9
+ // Stage 1's "renders cleanly" criterion is intentionally not repeated;
10
+ // if the app crashed Layer 2 build mode would have caught it.
11
+ export const DEFAULT_STAGE2_RUBRIC = [
12
+ {
13
+ id: "domain-content",
14
+ question: "Does this screen show the user's domain content (a list with at least one user-created entry, or a detail view of one such entry, with that entry's state badge visible)? PASS if there's a visible list/detail item the user just created. FAIL if it shows only a launch / welcome / login / signup / empty state.",
15
+ },
16
+ {
17
+ id: "no-substrate-leak",
18
+ question: "Is the screen free of substrate-original tokens like 'Shop', 'Shopkeeper', 'ItemTag', 'NativeAppTemplate', or 'Number Tag'?",
19
+ },
20
+ ];
21
+ export async function runStage2Visual(input) {
22
+ const screenshotDir = input.screenshotDir ?? join(process.cwd(), "tmp", "screenshots");
23
+ await mkdir(screenshotDir, { recursive: true });
24
+ const rubric = input.rubric ?? DEFAULT_STAGE2_RUBRIC;
25
+ const result = {};
26
+ if (input.iosScenario) {
27
+ result.ios = await runOnePlatform({
28
+ platform: "ios",
29
+ scenario: input.iosScenario,
30
+ screenshotDir,
31
+ spec: input.spec,
32
+ rubric,
33
+ ...(input.iosAppId !== undefined ? { appId: input.iosAppId } : {}),
34
+ ...(input.iosClient !== undefined ? { client: input.iosClient } : {}),
35
+ });
36
+ }
37
+ if (input.androidScenario) {
38
+ result.android = await runOnePlatform({
39
+ platform: "android",
40
+ scenario: input.androidScenario,
41
+ screenshotDir,
42
+ spec: input.spec,
43
+ rubric,
44
+ ...(input.androidAppId !== undefined ? { appId: input.androidAppId } : {}),
45
+ ...(input.androidClient !== undefined ? { client: input.androidClient } : {}),
46
+ });
47
+ }
48
+ return result;
49
+ }
50
+ async function runOnePlatform(args) {
51
+ const ownsClient = args.client === undefined;
52
+ const client = args.client ?? (await createMobileClient());
53
+ try {
54
+ // mobile-mcp requires a `device` argument on every tool call.
55
+ // Bootstrap: list available devices, pick the right one for this
56
+ // platform, set it as the active device on the wrapper. Subsequent
57
+ // calls inject device transparently.
58
+ const targetingErr = await selectDevice(client, args.platform);
59
+ if (targetingErr) {
60
+ return {
61
+ pass: false,
62
+ scenarioName: args.scenario.name,
63
+ stepCount: args.scenario.steps.length,
64
+ stepsPassed: 0,
65
+ screenshots: [],
66
+ error: targetingErr,
67
+ };
68
+ }
69
+ // Foreground the app before the walk. Between Stage 1 and Stage 2 the app
70
+ // can drop to springboard (observed: iOS Stage 2 step 0 saw home-screen
71
+ // icons, not "Welcome to"), so launch it explicitly first. Best-effort —
72
+ // a launch hiccup shouldn't abort Stage 2; the scenario's polling will
73
+ // surface a clear error if the app still isn't up.
74
+ if (args.appId) {
75
+ try {
76
+ await client.launchApp(args.appId);
77
+ await sleep(2_000); // let it come to front + render before the first poll
78
+ // If the launch hit the intermittent "Something went wrong" error
79
+ // screen, tap "Back to Start Screen" (logs out → welcome) before walking.
80
+ if (await recoverFromErrorScreen(client))
81
+ await sleep(1_500);
82
+ }
83
+ catch {
84
+ // ignore — scenario steps below report if the app isn't foregrounded
85
+ }
86
+ }
87
+ const scenario = await runStage2Scenario({
88
+ client,
89
+ scenario: args.scenario,
90
+ screenshotDir: args.screenshotDir,
91
+ });
92
+ const baseReport = toBaseReport(scenario);
93
+ // Run Layer 3 on the LAST captured screenshot even if a later
94
+ // scenario step failed — a partial walk that reached the domain
95
+ // content is still worth scoring. Stage2PlatformReport.pass
96
+ // remains gated on scenario.ok && layer3.pass below, so partial
97
+ // walks don't sneak through as overall PASS.
98
+ if (scenario.screenshots.length === 0) {
99
+ return baseReport;
100
+ }
101
+ const representative = scenario.screenshots[scenario.screenshots.length - 1];
102
+ const layer3 = await runLayer3({
103
+ screenshotPath: representative,
104
+ rubric: args.rubric,
105
+ spec: args.spec,
106
+ });
107
+ return {
108
+ ...baseReport,
109
+ pass: scenario.ok && layer3.pass,
110
+ representativeScreenshot: representative,
111
+ layer3Scores: layer3.scores,
112
+ };
113
+ }
114
+ finally {
115
+ if (ownsClient)
116
+ await client.close();
117
+ }
118
+ }
119
+ // Picks the mobile-mcp device for a platform and sets it as active on
120
+ // the wrapper. Returns undefined on success, an error string on
121
+ // failure. Heuristic: filter listDevices() by platform field if
122
+ // available; otherwise filter by a substring match on the name (iOS
123
+ // names tend to start with "iPhone"/"iPad"; Android emulators with
124
+ // "emulator-"). Honors NATIVEAPPTEMPLATE_MOBILE_<IOS|ANDROID>_DEVICE
125
+ // env var as an override for explicit targeting.
126
+ export async function selectDevice(client, platform) {
127
+ const overrideKey = platform === "ios" ? "NATIVEAPPTEMPLATE_MOBILE_IOS_DEVICE" : "NATIVEAPPTEMPLATE_MOBILE_ANDROID_DEVICE";
128
+ const override = process.env[overrideKey];
129
+ let devices;
130
+ try {
131
+ devices = await client.listDevices();
132
+ }
133
+ catch (err) {
134
+ // Discovery failed. If an override is set, fall back to using it raw as a
135
+ // best-effort escape hatch; otherwise surface the failure.
136
+ if (override) {
137
+ client.useDevice(override);
138
+ return undefined;
139
+ }
140
+ return `mobile-mcp listDevices failed: ${err instanceof Error ? err.message : String(err)}`;
141
+ }
142
+ if (override) {
143
+ // Resolve the override against the device list, matching either the
144
+ // canonical id or the display name, and pass the id onward. mobile-mcp's
145
+ // per-device tools require the id (UDID for iOS sims, serial for Android);
146
+ // a display name like "iPhone 17" passed raw fails every call with
147
+ // "Device not found" (and the wrapper reads that as 0 elements). Map
148
+ // name -> id here instead of trusting the override string blindly.
149
+ const hit = devices.find((d) => deviceMatchesHandle(d, override));
150
+ if (hit) {
151
+ const id = deviceNameOf(hit);
152
+ if (id) {
153
+ client.useDevice(id);
154
+ return undefined;
155
+ }
156
+ }
157
+ const seen = devices.map((d) => deviceNameOf(d) ?? "<unnamed>").join(", ");
158
+ return `${overrideKey}="${override}" matched no booted device (saw: ${seen}). Pass the device id or its exact name.`;
159
+ }
160
+ if (devices.length === 0) {
161
+ return `no mobile-mcp devices available (boot the ${platform} sim/emulator first)`;
162
+ }
163
+ const match = devices.find((d) => devicePlatformMatches(d, platform));
164
+ if (!match) {
165
+ const names = devices.map((d) => deviceNameOf(d) ?? "<unnamed>").join(", ");
166
+ return `no mobile-mcp device matched platform=${platform} (saw: ${names}). Override with ${overrideKey}=<device-name-or-id>.`;
167
+ }
168
+ const name = deviceNameOf(match);
169
+ if (!name) {
170
+ return `mobile-mcp returned a matching device with no usable name field`;
171
+ }
172
+ client.useDevice(name);
173
+ return undefined;
174
+ }
175
+ // True if `handle` equals any of the device's identifying fields — used to
176
+ // resolve a user-supplied override (which may be a display name OR an id)
177
+ // against mobile-mcp's device list.
178
+ function deviceMatchesHandle(d, handle) {
179
+ for (const key of ["id", "udid", "serial", "name", "deviceName"]) {
180
+ if (d[key] === handle)
181
+ return true;
182
+ }
183
+ return false;
184
+ }
185
+ function deviceNameOf(d) {
186
+ // mobile-mcp's response carries both `id` (canonical handle — UDID
187
+ // for iOS sims, serial for Android emulators/devices) and `name`
188
+ // (display name like "Pixel 6", "iPhone 17"). Pass the canonical id
189
+ // to the `device` parameter on subsequent tool calls — it's
190
+ // unambiguous when multiple devices share a display name.
191
+ for (const key of ["id", "udid", "serial", "name", "deviceName"]) {
192
+ const v = d[key];
193
+ if (typeof v === "string" && v.length > 0)
194
+ return v;
195
+ }
196
+ return undefined;
197
+ }
198
+ function devicePlatformMatches(d, platform) {
199
+ // Direct platform field is the easy case.
200
+ for (const key of ["platform", "type", "os"]) {
201
+ const v = d[key];
202
+ if (typeof v === "string" && v.toLowerCase().includes(platform))
203
+ return true;
204
+ }
205
+ // Fallback: heuristic on the name.
206
+ const name = deviceNameOf(d) ?? "";
207
+ if (platform === "ios")
208
+ return /^(iPhone|iPad|iPod)/i.test(name);
209
+ return /^(emulator-|Android|Pixel|Galaxy|Nexus)/i.test(name);
210
+ }
211
+ function toBaseReport(scenario) {
212
+ const stepsPassed = scenario.steps.filter((s) => s.ok).length;
213
+ const failingError = scenario.steps.find((s) => !s.ok)?.error;
214
+ const report = {
215
+ pass: scenario.ok,
216
+ scenarioName: scenario.scenarioName,
217
+ stepCount: scenario.steps.length,
218
+ stepsPassed,
219
+ screenshots: scenario.screenshots,
220
+ };
221
+ if (failingError !== undefined)
222
+ report.error = failingError;
223
+ return report;
224
+ }
225
+ //# sourceMappingURL=stage2-judge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stage2-judge.js","sourceRoot":"","sources":["../../src/validation/stage2-judge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ import type { MobileClient } from "../mobile.js";
2
+ export type Stage2Step = {
3
+ kind: "wait_for_text";
4
+ text: string;
5
+ timeoutMs?: number;
6
+ exact?: boolean;
7
+ } | {
8
+ kind: "tap_text";
9
+ text: string;
10
+ timeoutMs?: number;
11
+ optional?: boolean;
12
+ exact?: boolean;
13
+ } | {
14
+ kind: "tap_coordinates";
15
+ x: number;
16
+ y: number;
17
+ label?: string;
18
+ } | {
19
+ kind: "tap_below_text";
20
+ text: string;
21
+ offsetY?: number;
22
+ timeoutMs?: number;
23
+ } | {
24
+ kind: "type";
25
+ text: string;
26
+ submit?: boolean;
27
+ } | {
28
+ kind: "press_button";
29
+ button: string;
30
+ optional?: boolean;
31
+ } | {
32
+ kind: "screenshot";
33
+ label: string;
34
+ } | {
35
+ kind: "assert_text";
36
+ text: string;
37
+ } | {
38
+ kind: "rails_runner";
39
+ outDir: string;
40
+ ruby: string;
41
+ label?: string;
42
+ timeoutMs?: number;
43
+ } | {
44
+ kind: "tap_field";
45
+ fieldTypes: readonly string[];
46
+ nth?: number;
47
+ };
48
+ export type Stage2Scenario = {
49
+ name: string;
50
+ steps: readonly Stage2Step[];
51
+ };
52
+ export type Stage2StepResult = {
53
+ step: Stage2Step;
54
+ ok: boolean;
55
+ durationMs: number;
56
+ error?: string;
57
+ screenshotPath?: string;
58
+ };
59
+ export type Stage2Result = {
60
+ ok: boolean;
61
+ scenarioName: string;
62
+ steps: readonly Stage2StepResult[];
63
+ screenshots: readonly string[];
64
+ };
65
+ export type Stage2Input = {
66
+ client: MobileClient;
67
+ scenario: Stage2Scenario;
68
+ screenshotDir: string;
69
+ pollIntervalMs?: number;
70
+ defaultWaitMs?: number;
71
+ };
72
+ export declare function runStage2Scenario(input: Stage2Input): Promise<Stage2Result>;
73
+ export declare function recoverFromErrorScreen(client: MobileClient): Promise<boolean>;
74
+ //# sourceMappingURL=stage2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stage2.d.ts","sourceRoot":"","sources":["../../src/validation/stage2.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAiB,MAAM,cAAc,CAAC;AAsBhE,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAS5E;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAC3F;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAKjE;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAI9E;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAOrC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAW1F;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvE,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB,EAAE,EAAE,OAAO,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACnC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,cAAc,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAKF,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAkDjF;AA8JD,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,CAQnF"}