@aarwitz/tapp 0.17.20 → 0.17.21

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tapp",
3
3
  "description": "Give Claude hands and eyes on iOS, Android, and web apps, with exploration, replayable flows, evidence, and deterministic CI gates.",
4
- "version": "0.17.20",
4
+ "version": "0.17.21",
5
5
  "author": {
6
6
  "name": "Aaron Horowitz",
7
7
  "url": "https://github.com/aarwitz"
@@ -24,7 +24,7 @@
24
24
  "command": "npx",
25
25
  "args": [
26
26
  "-y",
27
- "@aarwitz/tapp@0.17.20",
27
+ "@aarwitz/tapp@0.17.21",
28
28
  "mcp"
29
29
  ],
30
30
  "cwd": "${CLAUDE_PROJECT_DIR}"
package/AGENTS.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Tapp agent playbook
2
2
 
3
3
  You (the agent) have Tapp: hands and eyes on real app surfaces — iOS simulators, Android
4
- emulators/devices, plus (beta) web apps in a real browser. Release judgment belongs only to Tapp's
4
+ emulators/devices, plus web apps in a real browser. Release judgment belongs only to Tapp's
5
5
  repository-connected deterministic gate.
6
6
 
7
7
  ## No MCP connected? Just run the CLI
@@ -5172,12 +5172,23 @@ class ExplorerTests: XCTestCase {
5172
5172
  // deleting only the prefix, then prove it is empty before inserting new text.
5173
5173
  element.typeKey("a", modifierFlags: .command)
5174
5174
  element.typeText(XCUIKeyboardKey.delete.rawValue)
5175
- if hasContent(), let remaining = element.value as? String {
5176
- // Some simulator keyboard configurations ignore Command-A. Clear on both
5177
- // sides of the cursor in that case; backspace alone leaves the suffix intact.
5178
- let count = remaining.utf16.count
5179
- element.typeText(String(repeating: XCUIKeyboardKey.delete.rawValue, count: count)
5180
- + String(repeating: XCUIKeyboardKey.forwardDelete.rawValue, count: count))
5175
+ // Command-A and forward delete are hardware-keyboard keys, and the simulator's
5176
+ // hardware keyboard is disconnected by default — which is how CI runs. XCUITest
5177
+ // cannot press a key the software keyboard does not have: the selection never
5178
+ // happens, and forwardDelete's raw value (U+F728, a private-use character) is
5179
+ // INSERTED as literal text. Clearing a 54-character field therefore left it holding
5180
+ // 54 unrenderable glyphs and reported "could not be cleared". Backspace IS on the
5181
+ // software keyboard, so clear with backspace alone, re-anchoring the caret past the
5182
+ // end of the visible text each pass to reach a suffix that was scrolled out of view.
5183
+ var passes = 0
5184
+ while hasContent(), passes < 4 {
5185
+ passes += 1
5186
+ guard let remaining = element.value as? String else { break }
5187
+ element.coordinate(withNormalizedOffset: CGVector(dx: 0.97, dy: 0.5)).tap()
5188
+ element.typeText(String(repeating: XCUIKeyboardKey.delete.rawValue,
5189
+ count: remaining.utf16.count + 2))
5190
+ // An empty field reports its placeholder, so this compares unequal once cleared.
5191
+ if (element.value as? String) == remaining { break }
5181
5192
  }
5182
5193
  let deadline = Date().addingTimeInterval(1.0)
5183
5194
  while hasContent() && Date() < deadline { Thread.sleep(forTimeInterval: 0.1) }
package/bin/tapp.js CHANGED
@@ -163,7 +163,7 @@ const engineImport = () => import(path.join(packageRoot, "mcp-server", "src", "i
163
163
 
164
164
  function requireMacFor(what) {
165
165
  if (process.platform === "darwin") return;
166
- console.error(`❌ ${what} requires macOS (Xcode + iOS simulator). The web beta runs anywhere: npx -y @aarwitz/tapp@latest explore https://localhost:3000`);
166
+ console.error(`❌ ${what} requires macOS (Xcode + iOS simulator). Web runs anywhere: npx -y @aarwitz/tapp@latest explore https://localhost:3000`);
167
167
  process.exit(1);
168
168
  }
169
169
 
package/browser/app.css CHANGED
@@ -176,24 +176,35 @@ dialog { width:min(720px,calc(100vw - 40px)); max-height:min(760px,calc(100vh -
176
176
  }
177
177
 
178
178
  /* Desktop-parity dark product skin. The component structure remains shared by
179
- local and hosted Tapp; this palette follows the native Command Center. */
179
+ local and hosted Tapp; the palette comes from tokens.css so the hosted Studio,
180
+ the local Studio, and the macOS Command Center read as one product. */
180
181
  :root {
181
182
  color-scheme:dark;
182
- --ink:#edf2f8; --muted:#929ba8; --faint:#687280; --line:#303844; --paper:#171c24; --wash:#20262f;
183
- --green:#58a6ff; --green-2:#4f9cf3; --green-3:#7bb8ff; --lime:#102d4d; --amber:#e2a826;
184
- --amber-bg:#302817; --red:#ff5c57; --red-bg:#392125; --blue:#58a6ff; --sidebar:#0c1118;
185
- --shadow:0 18px 55px rgba(0,0,0,.28); --small-shadow:0 8px 24px rgba(0,0,0,.18);
183
+ --ink:var(--tapp-text); --muted:var(--tapp-text-soft); --faint:var(--tapp-text-faint);
184
+ --line:var(--tapp-border); --paper:var(--tapp-panel); --wash:var(--tapp-panel-2);
185
+ --green:var(--tapp-blue); --green-2:var(--tapp-blue-hover); --green-3:#7bb8ff;
186
+ --lime:var(--tapp-selected); --amber:var(--tapp-amber); --amber-bg:var(--tapp-amber-bg);
187
+ --red:var(--tapp-red); --red-bg:var(--tapp-red-bg); --blue:var(--tapp-blue);
188
+ --sidebar:var(--tapp-sidebar); --shadow:var(--tapp-shadow); --small-shadow:var(--tapp-shadow-small);
186
189
  }
187
- body { background:#0d1117; }
188
- .topbar { background:rgba(13,17,23,.96); border-color:var(--line); }
190
+ body { background:var(--tapp-bg); font-family:var(--tapp-font); }
191
+ code { font-family:var(--tapp-font-mono); }
192
+ .topbar { background:rgba(7,16,23,.96); border-color:var(--tapp-border-strong); }
189
193
  .brand-mark { width:auto; background:transparent; color:var(--green); font-family:inherit; font-size:24px; }
190
194
  .mode-pill,.status-chip,.repository-switcher,.source-card,.panel,.decision-card,.target-card,.field,
191
195
  .live-frame-shell,.live-controls,.coverage-summary,.transition-list,.plan-item,.pipeline article,
192
196
  .evidence-card,.search-input,.github-repositories,.github-repositories button,.manual-action input,
193
197
  .run-row,.finding-row,.live-control { background:var(--paper); color:var(--ink); border-color:var(--line); }
194
198
  .source-onboarding { min-height:calc(100vh - 64px); }
195
- .source-card:hover,.source-card.dragging { border-color:var(--green-2); box-shadow:0 20px 56px rgba(0,0,0,.36); }
196
- .source-card.dragging,.target-card.selected,.run-row.selected,.finding-row.selected { background:#132237; }
199
+ .source-card:hover,.source-card.dragging { border-color:var(--green-2); box-shadow:0 20px 56px rgba(0,0,0,.46); }
200
+ .source-card.dragging,.target-card.selected,.run-row.selected,.finding-row.selected { background:var(--tapp-selected); }
201
+ /* Controls match the Command Center: compact, 8px radius, muted surface. */
202
+ button { border-radius:var(--tapp-radius); }
203
+ button.primary { background:var(--tapp-blue); color:#fff; }
204
+ button.primary:hover { background:var(--tapp-blue-hover); }
205
+ button.secondary { color:var(--tapp-text-control); background:var(--tapp-control); border-color:var(--tapp-border); }
206
+ button.secondary:hover,button.quiet:hover { background:var(--tapp-panel-3); }
207
+ button.quiet { border-color:var(--tapp-border); color:var(--muted); }
197
208
  .platform-row span,.button-link { background:#131a23; border-color:#385a7c; color:var(--green); }
198
209
  button.primary { background:var(--green); color:#07111d; }
199
210
  button.primary:hover { background:#79b8ff; }
@@ -7,6 +7,7 @@
7
7
  <meta name="tapp-api-base" content="">
8
8
  <meta name="tapp-login-url" content="">
9
9
  <title>Tapp</title>
10
+ <link rel="stylesheet" href="./assets/tokens.css">
10
11
  <link rel="stylesheet" href="./assets/app.css">
11
12
  </head>
12
13
  <body>
@@ -0,0 +1,50 @@
1
+ /* Tapp design tokens — the single source of truth for what Tapp looks like.
2
+ Extracted from the Command Center shell (website/) so the hosted Studio
3
+ (app.runtapp.com), the local `tapp app` Studio, and the macOS app read as one
4
+ product. Surfaces consume these variables; none should hardcode a palette. */
5
+ :root {
6
+ color-scheme: dark;
7
+
8
+ /* Ground and surfaces, darkest to lightest */
9
+ --tapp-bg: #071017;
10
+ --tapp-bg-deep: #040b11;
11
+ --tapp-sidebar: #0b1218;
12
+ --tapp-sidebar-solid: #0c1117;
13
+ --tapp-panel: #0f1a22;
14
+ --tapp-panel-2: #111d26;
15
+ --tapp-panel-3: #192730;
16
+ --tapp-panel-soft: rgba(17, 22, 28, 0.8);
17
+ --tapp-control: #1a222b;
18
+
19
+ /* Lines */
20
+ --tapp-border: rgba(168, 182, 196, 0.12);
21
+ --tapp-border-strong: #343b43;
22
+
23
+ /* Type */
24
+ --tapp-text: #edf3f7;
25
+ --tapp-text-soft: #b7c6d1;
26
+ --tapp-text-faint: #7e8d99;
27
+ --tapp-text-control: #dce3e9;
28
+
29
+ /* Accent and status */
30
+ --tapp-blue: #1f79ff;
31
+ --tapp-blue-hover: #2d87ff;
32
+ --tapp-amber: #e2a826;
33
+ --tapp-amber-bg: #302817;
34
+ --tapp-red: #ff5c57;
35
+ --tapp-red-bg: #392125;
36
+ --tapp-selected: #132b44;
37
+
38
+ /* Depth */
39
+ --tapp-shadow: 0 18px 55px rgba(0, 0, 0, 0.42);
40
+ --tapp-shadow-small: 0 8px 24px rgba(0, 0, 0, 0.3);
41
+
42
+ /* Type families — Inter for UI, SF Mono for evidence and identifiers */
43
+ --tapp-font: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
44
+ --tapp-font-mono: "SFMono-Regular", ui-monospace, Consolas, Menlo, monospace;
45
+
46
+ /* Shell metrics */
47
+ --tapp-sidebar-width: 220px;
48
+ --tapp-radius: 8px;
49
+ --tapp-radius-lg: 12px;
50
+ }
@@ -2729,7 +2729,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2729
2729
  title: "Explore (autonomous)",
2730
2730
  description:
2731
2731
  "Autonomously explore iOS (appBundleId), Android (androidAppId), OR a web app " +
2732
- "(url — beta, requires Playwright installed) and return a structured EXPLORATION OBSERVATION. " +
2732
+ "(url — requires Playwright installed) and return a structured EXPLORATION OBSERVATION. " +
2733
2733
  "Exploration OBSERVES — it surfaces findings + coverage + evidence; it does NOT render a ship " +
2734
2734
  "verdict or score. To gate a merge, run the deterministic gate (contracts + baseline via CI). " +
2735
2735
  "Use when the user wants to find bugs / observe what breaks — this " +
@@ -2756,7 +2756,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2756
2756
  apkPath: { type: "string", description: "Android: optional APK to install before testing." },
2757
2757
  androidSerial: { type: "string", description: "Android: optional adb device serial; defaults to the first authorized device." },
2758
2758
  clearData: { type: "boolean", default: true, description: "Android: clear app data before launch for a repeatable starting state." },
2759
- url: { type: "string", description: "Web (beta): URL of the app to explore in a real browser (same-origin only; your own app/staging). Provide exactly one of appBundleId | url." },
2759
+ url: { type: "string", description: "Web: URL of the app to explore in a real browser (same-origin only; your own app/staging). Provide exactly one of appBundleId | url." },
2760
2760
  watch: { type: "boolean", default: false, description: "Web only: open Tapp's controlled Chromium window and show a cursor/HUD for each exploration action. Evidence screenshots exclude the overlay." },
2761
2761
  device: { type: "string", description: "Web only: render as a Playwright device profile (e.g. \"iPhone 13\") — viewport, user agent, touch" },
2762
2762
  viewport: { type: "string", description: "Web only: explicit viewport WIDTHxHEIGHT (e.g. \"390x844\"); overrides the device profile's viewport" },
@@ -3616,7 +3616,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3616
3616
  const wantsAndroid = isNonEmptyString(args.androidAppId);
3617
3617
  const targets = [wantsWeb, wantsAndroid, isNonEmptyString(args.appBundleId)].filter(Boolean).length;
3618
3618
  if (targets !== 1) {
3619
- return errorResult("Provide exactly one of appBundleId (iOS), androidAppId (Android), or url (web beta)");
3619
+ return errorResult("Provide exactly one of appBundleId (iOS), androidAppId (Android), or url (web)");
3620
3620
  }
3621
3621
  if (args.watch === true && !wantsWeb) return errorResult("watch is currently available for web exploration only");
3622
3622
 
@@ -362,7 +362,7 @@ export function buildQaReport(markersFilePath, { platform = "ios", target = null
362
362
  "privacy or API data minimization",
363
363
  "brand and SEO consistency",
364
364
  "visual credibility or asset quality (vision review; needs an API key)",
365
- "only the first few visible buttons per page are probed (web beta)",
365
+ "only the first few visible buttons per page are probed (web)",
366
366
  "content & reachability regressions require a baseline",
367
367
  ];
368
368
  if (outbound?.mailtoSkipped === "egress-policy") notChecked.push("mailto address domains (MX lookups are skipped by the public-egress policy)");
@@ -1,4 +1,4 @@
1
- // Web exploration driver (beta) — the second platform behind the OCQA marker protocol.
1
+ // Web exploration driver — the second platform behind the OCQA marker protocol.
2
2
  //
3
3
  // Drives a real browser via Playwright and emits the SAME OCQA_* marker lines the iOS
4
4
  // XCUITest harness emits, into the same captures/<id>/ocqa-markers.txt layout — so the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aarwitz/tapp",
3
- "version": "0.17.20",
3
+ "version": "0.17.21",
4
4
  "mcpName": "io.github.aarwitz/tapp",
5
5
  "description": "Let coding agents verify UI changes on real iOS, Android, and web surfaces, then enforce reviewed proof in deterministic CI.",
6
6
  "license": "MIT",