@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.
- package/.claude-plugin/plugin.json +2 -2
- package/AGENTS.md +1 -1
- package/Harness/OCQAHarnessUITests/ExplorerTests.swift +17 -6
- package/bin/tapp.js +1 -1
- package/browser/app.css +20 -9
- package/browser/index.html +1 -0
- package/browser/tokens.css +50 -0
- package/mcp-server/src/index.js +3 -3
- package/mcp-server/src/report.js +1 -1
- package/mcp-server/src/web-explorer.js +1 -1
- package/package.json +1 -1
|
@@ -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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
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).
|
|
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;
|
|
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
|
|
183
|
-
--
|
|
184
|
-
--
|
|
185
|
-
--
|
|
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
|
|
188
|
-
|
|
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,.
|
|
196
|
-
.source-card.dragging,.target-card.selected,.run-row.selected,.finding-row.selected { background
|
|
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; }
|
package/browser/index.html
CHANGED
|
@@ -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
|
+
}
|
package/mcp-server/src/index.js
CHANGED
|
@@ -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 —
|
|
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
|
|
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
|
|
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
|
|
package/mcp-server/src/report.js
CHANGED
|
@@ -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
|
|
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
|
|
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.
|
|
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",
|