@swmansion/argent 0.15.1-next.13 → 0.15.1-next.2
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/README.md +3 -12
- package/bin/darwin/simulator-server +0 -0
- package/bin/linux/simulator-server +0 -0
- package/bin/linux-arm64/simulator-server +0 -0
- package/dist/bundled-paths.js +2 -3
- package/dist/bundled-paths.js.map +1 -1
- package/dist/cli-cmds.mjs +6 -39
- package/dist/installer.mjs +34 -416
- package/dist/mcp-server.mjs +3 -35
- package/dist/tool-server.cjs +152 -545
- package/package.json +5 -3
- package/rules/argent.md +2 -2
- package/scripts/postinstall.cjs +105 -0
- package/skills/argent-create-flow/SKILL.md +16 -22
- package/skills/argent-device-interact/SKILL.md +0 -12
- package/skills/argent-metro-debugger/SKILL.md +8 -11
- package/skills/argent-metro-debugger/references/failure-scenarios.md +6 -6
- package/skills/argent-test-ui-flow/SKILL.md +1 -3
- package/skills/argent-tv-interact/SKILL.md +1 -11
package/dist/tool-server.cjs
CHANGED
|
@@ -314,10 +314,10 @@ var init_failure_codes = __esm({
|
|
|
314
314
|
ANDROID_UIAUTOMATOR_PARSE_FAILED: "ANDROID_UIAUTOMATOR_PARSE_FAILED",
|
|
315
315
|
ANDROID_UIAUTOMATOR_CAPTURE_FAILED: "ANDROID_UIAUTOMATOR_CAPTURE_FAILED",
|
|
316
316
|
DEBUGGER_METRO_NOT_RUNNING: "DEBUGGER_METRO_NOT_RUNNING",
|
|
317
|
+
DEBUGGER_METRO_PROJECT_ROOT_MISSING: "DEBUGGER_METRO_PROJECT_ROOT_MISSING",
|
|
317
318
|
DEBUGGER_METRO_NO_TARGETS: "DEBUGGER_METRO_NO_TARGETS",
|
|
318
319
|
DEBUGGER_CDP_RUNTIME_EXCEPTION: "DEBUGGER_CDP_RUNTIME_EXCEPTION",
|
|
319
320
|
DEBUGGER_CDP_BINDING_TIMEOUT: "DEBUGGER_CDP_BINDING_TIMEOUT",
|
|
320
|
-
DEBUGGER_CDP_BINDING_UNAVAILABLE: "DEBUGGER_CDP_BINDING_UNAVAILABLE",
|
|
321
321
|
DEBUGGER_CDP_PROTOCOL_ERROR: "DEBUGGER_CDP_PROTOCOL_ERROR",
|
|
322
322
|
DEBUGGER_RELOAD_FAILED: "DEBUGGER_RELOAD_FAILED",
|
|
323
323
|
JS_RUNTIME_CONSOLE_SERVER_BIND_FAILED: "JS_RUNTIME_CONSOLE_SERVER_BIND_FAILED",
|
|
@@ -404,7 +404,6 @@ var init_failure_codes = __esm({
|
|
|
404
404
|
CHROMIUM_ELECTRON_EXITED_BEFORE_READY: "CHROMIUM_ELECTRON_EXITED_BEFORE_READY",
|
|
405
405
|
KEYBOARD_KEY_UNSUPPORTED: "KEYBOARD_KEY_UNSUPPORTED",
|
|
406
406
|
KEYBOARD_CHARACTER_UNSUPPORTED: "KEYBOARD_CHARACTER_UNSUPPORTED",
|
|
407
|
-
SECRET_PLACEHOLDER_UNKNOWN: "SECRET_PLACEHOLDER_UNKNOWN",
|
|
408
407
|
SCREENSHOT_DIFF_INPUT_INVALID: "SCREENSHOT_DIFF_INPUT_INVALID",
|
|
409
408
|
BOOT_DEVICE_TARGET_SELECTION_INVALID: "BOOT_DEVICE_TARGET_SELECTION_INVALID",
|
|
410
409
|
BOOT_IOS_UNSUPPORTED_HOST: "BOOT_IOS_UNSUPPORTED_HOST",
|
|
@@ -103836,8 +103835,6 @@ var ALLOWED = {
|
|
|
103836
103835
|
action: PACKAGE_ACTION,
|
|
103837
103836
|
is_success: bool,
|
|
103838
103837
|
duration_ms: DURATION_MS,
|
|
103839
|
-
retry_count: COUNT,
|
|
103840
|
-
last_attempt_duration_ms: DURATION_MS,
|
|
103841
103838
|
...FAILURE_SIGNAL2
|
|
103842
103839
|
},
|
|
103843
103840
|
"installation:cli_update_start": {},
|
|
@@ -104399,7 +104396,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
104399
104396
|
var SESSION_ID = (0, import_node_crypto3.randomUUID)();
|
|
104400
104397
|
function readCliVersion() {
|
|
104401
104398
|
if (true) {
|
|
104402
|
-
return "0.
|
|
104399
|
+
return "0.15.0";
|
|
104403
104400
|
}
|
|
104404
104401
|
return "0.0.0";
|
|
104405
104402
|
}
|
|
@@ -105764,7 +105761,7 @@ var import_node_path4 = __toESM(require("node:path"));
|
|
|
105764
105761
|
var import_semver = __toESM(require_semver2());
|
|
105765
105762
|
|
|
105766
105763
|
// ../tool-server/package.json
|
|
105767
|
-
var version3 = "0.
|
|
105764
|
+
var version3 = "0.15.0";
|
|
105768
105765
|
|
|
105769
105766
|
// ../tool-server/src/utils/update-checker.ts
|
|
105770
105767
|
var import_update_core = __toESM(require_dist4());
|
|
@@ -114409,8 +114406,6 @@ var CDPClient = class {
|
|
|
114409
114406
|
nextId = 1;
|
|
114410
114407
|
pending = /* @__PURE__ */ new Map();
|
|
114411
114408
|
pendingBindings = /* @__PURE__ */ new Map();
|
|
114412
|
-
/** Set by addBinding when the runtime ACKs the command but installs nothing. */
|
|
114413
|
-
bindingUnavailable = false;
|
|
114414
114409
|
scripts = /* @__PURE__ */ new Map();
|
|
114415
114410
|
enabledDomains = /* @__PURE__ */ new Set();
|
|
114416
114411
|
wsUrl;
|
|
@@ -114543,8 +114538,6 @@ var CDPClient = class {
|
|
|
114543
114538
|
}
|
|
114544
114539
|
async addBinding(name) {
|
|
114545
114540
|
await this.send("Runtime.addBinding", { name });
|
|
114546
|
-
const probe3 = await this.evaluate(`typeof ${name}`).catch(() => void 0);
|
|
114547
|
-
this.bindingUnavailable = probe3 === "undefined";
|
|
114548
114541
|
}
|
|
114549
114542
|
/**
|
|
114550
114543
|
* Inject a script that will push a result via the binding using a unique requestId.
|
|
@@ -114553,19 +114546,6 @@ var CDPClient = class {
|
|
|
114553
114546
|
evaluateWithBinding(expression, requestId, options) {
|
|
114554
114547
|
const id = requestId ?? crypto3.randomUUID();
|
|
114555
114548
|
const timeout = options?.timeout ?? DEFAULT_TIMEOUT_MS2;
|
|
114556
|
-
if (this.bindingUnavailable) {
|
|
114557
|
-
return Promise.reject(
|
|
114558
|
-
new FailureError(
|
|
114559
|
-
"This JS runtime acknowledges Runtime.addBinding but never installs the binding (legacy Hermes, React Native <= 0.72), so it cannot deliver a result over the binding channel. Tools that read the React tree this way are unavailable here; use `describe` to read on-screen structure.",
|
|
114560
|
-
{
|
|
114561
|
-
error_code: FAILURE_CODES.DEBUGGER_CDP_BINDING_UNAVAILABLE,
|
|
114562
|
-
failure_stage: "debugger_cdp_binding",
|
|
114563
|
-
failure_area: "tool_server",
|
|
114564
|
-
error_kind: "unsupported"
|
|
114565
|
-
}
|
|
114566
|
-
)
|
|
114567
|
-
);
|
|
114568
|
-
}
|
|
114569
114549
|
return new Promise((resolve7, reject) => {
|
|
114570
114550
|
const timer = setTimeout(() => {
|
|
114571
114551
|
this.pendingBindings.delete(id);
|
|
@@ -122002,14 +121982,12 @@ init_src();
|
|
|
122002
121982
|
|
|
122003
121983
|
// ../tool-server/src/utils/debugger/discovery.ts
|
|
122004
121984
|
init_src();
|
|
122005
|
-
var DECOY_VM = "don't use";
|
|
122006
121985
|
async function discoverMetro(port) {
|
|
122007
|
-
|
|
122008
|
-
|
|
122009
|
-
|
|
122010
|
-
} catch (err) {
|
|
121986
|
+
const statusRes = await fetch(`http://localhost:${port}/status`);
|
|
121987
|
+
const statusText = await statusRes.text();
|
|
121988
|
+
if (!statusText.includes("packager-status:running")) {
|
|
122011
121989
|
throw new FailureError(
|
|
122012
|
-
`Metro at port ${port} is not running (got: ${
|
|
121990
|
+
`Metro at port ${port} is not running (got: ${statusText.slice(0, 100)})`,
|
|
122013
121991
|
{
|
|
122014
121992
|
error_code: FAILURE_CODES.DEBUGGER_METRO_NOT_RUNNING,
|
|
122015
121993
|
failure_stage: "debugger_discover_metro_status",
|
|
@@ -122018,25 +121996,21 @@ async function discoverMetro(port) {
|
|
|
122018
121996
|
}
|
|
122019
121997
|
);
|
|
122020
121998
|
}
|
|
122021
|
-
const
|
|
122022
|
-
if (!
|
|
121999
|
+
const projectRoot = statusRes.headers.get("X-React-Native-Project-Root") ?? "";
|
|
122000
|
+
if (!projectRoot) {
|
|
122023
122001
|
throw new FailureError(
|
|
122024
|
-
`Metro at port ${port}
|
|
122002
|
+
`Metro at port ${port} did not return X-React-Native-Project-Root header`,
|
|
122025
122003
|
{
|
|
122026
|
-
error_code: FAILURE_CODES.
|
|
122027
|
-
failure_stage: "
|
|
122004
|
+
error_code: FAILURE_CODES.DEBUGGER_METRO_PROJECT_ROOT_MISSING,
|
|
122005
|
+
failure_stage: "debugger_discover_metro_project_root",
|
|
122028
122006
|
failure_area: "tool_server",
|
|
122029
122007
|
error_kind: "network"
|
|
122030
122008
|
}
|
|
122031
122009
|
);
|
|
122032
122010
|
}
|
|
122033
|
-
const projectRoot = statusRes.headers.get("X-React-Native-Project-Root") ?? "";
|
|
122034
122011
|
const listRes = await fetch(`http://localhost:${port}/json/list`);
|
|
122035
|
-
const
|
|
122036
|
-
|
|
122037
|
-
(t) => t?.vm !== DECOY_VM
|
|
122038
|
-
);
|
|
122039
|
-
if (!targets.length) {
|
|
122012
|
+
const targets = await listRes.json();
|
|
122013
|
+
if (!targets?.length) {
|
|
122040
122014
|
throw new FailureError(
|
|
122041
122015
|
`Metro at port ${port} has no CDP targets \u2014 is a React Native app connected?`,
|
|
122042
122016
|
{
|
|
@@ -122051,19 +122025,8 @@ async function discoverMetro(port) {
|
|
|
122051
122025
|
}
|
|
122052
122026
|
|
|
122053
122027
|
// ../tool-server/src/utils/debugger/target-selection.ts
|
|
122054
|
-
function deviceKey(target) {
|
|
122055
|
-
const logicalId = target.reactNative?.logicalDeviceId;
|
|
122056
|
-
if (logicalId) return logicalId;
|
|
122057
|
-
try {
|
|
122058
|
-
const device = new URL(target.webSocketDebuggerUrl).searchParams.get("device");
|
|
122059
|
-
if (device) return `device=${device}`;
|
|
122060
|
-
} catch {
|
|
122061
|
-
}
|
|
122062
|
-
return target.deviceName;
|
|
122063
|
-
}
|
|
122064
122028
|
function selectTarget(targets, port, options) {
|
|
122065
|
-
|
|
122066
|
-
let candidates = pool;
|
|
122029
|
+
let candidates = targets;
|
|
122067
122030
|
if (typeof options?.deviceId === "string" && options.deviceId) {
|
|
122068
122031
|
const deviceId = options.deviceId;
|
|
122069
122032
|
const filtered = candidates.filter((t) => t.reactNative?.logicalDeviceId === deviceId);
|
|
@@ -122071,21 +122034,16 @@ function selectTarget(targets, port, options) {
|
|
|
122071
122034
|
candidates = filtered;
|
|
122072
122035
|
} else {
|
|
122073
122036
|
const distinctDevices = /* @__PURE__ */ new Map();
|
|
122074
|
-
for (const t of
|
|
122075
|
-
const
|
|
122076
|
-
if (
|
|
122077
|
-
distinctDevices.set(
|
|
122078
|
-
name: t.deviceName,
|
|
122079
|
-
logicalId: t.reactNative?.logicalDeviceId
|
|
122080
|
-
});
|
|
122037
|
+
for (const t of targets) {
|
|
122038
|
+
const id = t.reactNative?.logicalDeviceId;
|
|
122039
|
+
if (id !== void 0 && id !== "" && !distinctDevices.has(id)) {
|
|
122040
|
+
distinctDevices.set(id, t.deviceName);
|
|
122081
122041
|
}
|
|
122082
122042
|
}
|
|
122083
122043
|
if (distinctDevices.size > 1) {
|
|
122084
|
-
const listed = [...distinctDevices.
|
|
122085
|
-
(d) => d.logicalId ? `${d.name ?? "unknown"} (${d.logicalId})` : `${d.name ?? "unknown"} (legacy inspector \u2014 no logicalDeviceId)`
|
|
122086
|
-
).join(", ");
|
|
122044
|
+
const listed = [...distinctDevices.entries()].map(([id, name]) => name ? `${name} (${id})` : id).join(", ");
|
|
122087
122045
|
throw new Error(
|
|
122088
|
-
`No debugger target matches device_id "${deviceId}". ${distinctDevices.size} devices are connected to Metro on port ${port}: ${listed}.
|
|
122046
|
+
`No debugger target matches device_id "${deviceId}". ${distinctDevices.size} devices are connected to Metro on port ${port}: ${listed}. Pass the logicalDeviceId (in parentheses) of the desired device as device_id (the logicalDeviceId returned by debugger-connect).`
|
|
122089
122047
|
);
|
|
122090
122048
|
}
|
|
122091
122049
|
}
|
|
@@ -122226,7 +122184,6 @@ function parseNode(line) {
|
|
|
122226
122184
|
// ../tool-server/src/utils/debugger/source-resolver.ts
|
|
122227
122185
|
var ALLOWED_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"]);
|
|
122228
122186
|
function isInsideProject(absFile, projectRoot) {
|
|
122229
|
-
if (!projectRoot) return false;
|
|
122230
122187
|
const resolvedRoot = path17.resolve(projectRoot);
|
|
122231
122188
|
const resolvedFile = path17.resolve(absFile);
|
|
122232
122189
|
const rel = path17.relative(resolvedRoot, resolvedFile);
|
|
@@ -122269,7 +122226,7 @@ function createSourceResolver(port, projectRoot) {
|
|
|
122269
122226
|
const frame = data.stack?.[0];
|
|
122270
122227
|
if (!frame?.file) return null;
|
|
122271
122228
|
if (/^https?:\/\//.test(frame.file)) return null;
|
|
122272
|
-
const relFile =
|
|
122229
|
+
const relFile = frame.file.replace(projectRoot + "/", "").replace(/^\/+/, "");
|
|
122273
122230
|
return {
|
|
122274
122231
|
file: relFile,
|
|
122275
122232
|
line: frame.lineNumber ?? 0,
|
|
@@ -122288,7 +122245,6 @@ function createSourceResolver(port, projectRoot) {
|
|
|
122288
122245
|
},
|
|
122289
122246
|
symbolicate: symbolicateFrame,
|
|
122290
122247
|
async readSourceFragment(location, contextLines = 3) {
|
|
122291
|
-
if (!projectRoot) return null;
|
|
122292
122248
|
try {
|
|
122293
122249
|
const absPath = path17.isAbsolute(location.file) ? path17.resolve(location.file) : path17.resolve(projectRoot, location.file);
|
|
122294
122250
|
const realRoot = await fs18.realpath(projectRoot);
|
|
@@ -126146,10 +126102,7 @@ var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
|
126146
126102
|
var zodSchema16 = external_exports.object({
|
|
126147
126103
|
udid: external_exports.string().describe("Target device id from `list-devices` (iOS UDID, Android serial, or Chromium id)."),
|
|
126148
126104
|
x: external_exports.number().describe("Normalized horizontal position 0.0\u20131.0 (left=0, right=1), not pixels"),
|
|
126149
|
-
y: external_exports.number().describe("Normalized vertical position 0.0\u20131.0 (top=0, bottom=1), not pixels")
|
|
126150
|
-
clickCount: external_exports.number().int().min(1).max(10).optional().describe(
|
|
126151
|
-
"Number of taps/clicks dispatched as ONE multi-tap gesture (2 = double-tap / double-click). The taps land inside the OS double-tap window; on Chromium each click carries an escalating CDP clickCount so dblclick actually fires. Default 1."
|
|
126152
|
-
)
|
|
126105
|
+
y: external_exports.number().describe("Normalized vertical position 0.0\u20131.0 (top=0, bottom=1), not pixels")
|
|
126153
126106
|
});
|
|
126154
126107
|
var capability8 = {
|
|
126155
126108
|
apple: { simulator: true, device: true },
|
|
@@ -126157,25 +126110,19 @@ var capability8 = {
|
|
|
126157
126110
|
android: { emulator: true, device: true, unknown: true },
|
|
126158
126111
|
chromium: { app: true }
|
|
126159
126112
|
};
|
|
126160
|
-
|
|
126161
|
-
var MULTI_TAP_GAP_MS = 100;
|
|
126162
|
-
async function tapChromium(api, x, y, clickCount) {
|
|
126113
|
+
async function tapChromium(api, x, y) {
|
|
126163
126114
|
const vp = api.getViewport();
|
|
126164
126115
|
const pxX = Math.max(0, Math.min(vp.width, x * vp.width));
|
|
126165
126116
|
const pxY = Math.max(0, Math.min(vp.height, y * vp.height));
|
|
126166
126117
|
await api.dispatchMouseEvent({ type: "mouseMoved", x: pxX, y: pxY });
|
|
126167
|
-
|
|
126168
|
-
|
|
126169
|
-
|
|
126170
|
-
await sleep2(TAP_HOLD_MS);
|
|
126171
|
-
await api.dispatchMouseEvent({ type: "mouseReleased", x: pxX, y: pxY, clickCount: i });
|
|
126172
|
-
}
|
|
126118
|
+
await api.dispatchMouseEvent({ type: "mousePressed", x: pxX, y: pxY, clickCount: 1 });
|
|
126119
|
+
await sleep2(50);
|
|
126120
|
+
await api.dispatchMouseEvent({ type: "mouseReleased", x: pxX, y: pxY, clickCount: 1 });
|
|
126173
126121
|
}
|
|
126174
126122
|
var gestureTapTool = {
|
|
126175
126123
|
id: "gesture-tap",
|
|
126176
126124
|
description: `Press the device screen (iOS simulator, Android emulator, or Chromium app) at normalized coordinates: x and y are fractions of screen width and height in 0.0\u20131.0 (not pixels).
|
|
126177
126125
|
Sends a Down event followed by an Up event at the same point. For Chromium, this dispatches a CDP mouse-press/release on the renderer.
|
|
126178
|
-
Set clickCount: 2 for a double-tap / double-click \u2014 the taps are dispatched as one gesture with proper click counting, which two separate tap calls cannot guarantee.
|
|
126179
126126
|
Use when you need to tap a button, link, or any tappable element on the screen.
|
|
126180
126127
|
Returns { tapped: true, timestampMs }. Fails if the simulator-server / emulator backend / Chromium CDP is not reachable for the given device.
|
|
126181
126128
|
Before tapping, determine the correct coordinates by using discovery tools \u2014 pick by platform: iOS / Android use \`describe\`, \`native-describe-screen\`, or \`debugger-component-tree\`; Chromium uses \`describe\` (the DOM walker), since the native and RN-specific discovery tools don't apply. More information in \`argent-device-interact\` skill`,
|
|
@@ -126193,33 +126140,29 @@ Before tapping, determine the correct coordinates by using discovery tools \u201
|
|
|
126193
126140
|
async execute(services, params) {
|
|
126194
126141
|
const device = resolveDevice(params.udid);
|
|
126195
126142
|
const timestampMs = Date.now();
|
|
126196
|
-
const clickCount = params.clickCount ?? 1;
|
|
126197
126143
|
if (device.platform === "chromium") {
|
|
126198
126144
|
const chromium = services.chromium;
|
|
126199
|
-
await tapChromium(chromium, params.x, params.y
|
|
126145
|
+
await tapChromium(chromium, params.x, params.y);
|
|
126200
126146
|
return { tapped: true, timestampMs };
|
|
126201
126147
|
}
|
|
126202
126148
|
const api = services.simulatorServer;
|
|
126203
|
-
|
|
126204
|
-
|
|
126205
|
-
|
|
126206
|
-
|
|
126207
|
-
|
|
126208
|
-
|
|
126209
|
-
|
|
126210
|
-
|
|
126211
|
-
|
|
126212
|
-
|
|
126213
|
-
|
|
126214
|
-
|
|
126215
|
-
|
|
126216
|
-
|
|
126217
|
-
|
|
126218
|
-
|
|
126219
|
-
|
|
126220
|
-
second_y: null
|
|
126221
|
-
});
|
|
126222
|
-
}
|
|
126149
|
+
sendCommand(api, {
|
|
126150
|
+
cmd: "touch",
|
|
126151
|
+
type: "Down",
|
|
126152
|
+
x: params.x,
|
|
126153
|
+
y: params.y,
|
|
126154
|
+
second_x: null,
|
|
126155
|
+
second_y: null
|
|
126156
|
+
});
|
|
126157
|
+
await sleep2(50);
|
|
126158
|
+
sendCommand(api, {
|
|
126159
|
+
cmd: "touch",
|
|
126160
|
+
type: "Up",
|
|
126161
|
+
x: params.x,
|
|
126162
|
+
y: params.y,
|
|
126163
|
+
second_x: null,
|
|
126164
|
+
second_y: null
|
|
126165
|
+
});
|
|
126223
126166
|
return { tapped: true, timestampMs };
|
|
126224
126167
|
}
|
|
126225
126168
|
};
|
|
@@ -126731,54 +126674,6 @@ Fails if the device backend is not reachable \u2014 the simulator-server for iOS
|
|
|
126731
126674
|
// ../tool-server/src/tools/keyboard/index.ts
|
|
126732
126675
|
init_zod();
|
|
126733
126676
|
|
|
126734
|
-
// ../tool-server/src/utils/secrets.ts
|
|
126735
|
-
init_src();
|
|
126736
|
-
var SECRET_ENV_PREFIX = "ARGENT_SECRET_";
|
|
126737
|
-
var SECRET_PLACEHOLDER_MARKER = "{{secret:";
|
|
126738
|
-
var PLACEHOLDER_RE = /\{\{secret:([A-Za-z_][A-Za-z0-9_]*)\}\}/g;
|
|
126739
|
-
function availableSecretNames(env = process.env) {
|
|
126740
|
-
return Object.keys(env).filter((k) => k.startsWith(SECRET_ENV_PREFIX) && env[k] !== void 0).map((k) => k.slice(SECRET_ENV_PREFIX.length)).sort();
|
|
126741
|
-
}
|
|
126742
|
-
var REDUNDANT_PREFIX_RE = /^argent_secret_/i;
|
|
126743
|
-
function resolveSecretPlaceholders(text, env = process.env) {
|
|
126744
|
-
const secrets = [];
|
|
126745
|
-
const resolved = text.replace(PLACEHOLDER_RE, (placeholder, rawName) => {
|
|
126746
|
-
let name = rawName;
|
|
126747
|
-
let value = env[SECRET_ENV_PREFIX + name];
|
|
126748
|
-
if (value === void 0 && REDUNDANT_PREFIX_RE.test(name)) {
|
|
126749
|
-
name = name.replace(REDUNDANT_PREFIX_RE, "");
|
|
126750
|
-
value = env[SECRET_ENV_PREFIX + name];
|
|
126751
|
-
}
|
|
126752
|
-
if (value === void 0) {
|
|
126753
|
-
const names = availableSecretNames(env);
|
|
126754
|
-
throw new InvalidToolInputError(
|
|
126755
|
-
`Unknown secret "${rawName}" \u2014 no ${SECRET_ENV_PREFIX}${name} environment variable is set on the machine running the tool-server. Available secrets: ${names.length ? names.join(", ") : "(none)"}. To make it available, ask the user to export ${SECRET_ENV_PREFIX}${name} in the tool-server's environment \u2014 never ask the user for the secret value itself.`,
|
|
126756
|
-
{
|
|
126757
|
-
error_code: FAILURE_CODES.SECRET_PLACEHOLDER_UNKNOWN,
|
|
126758
|
-
failure_stage: "secret_placeholder_resolution",
|
|
126759
|
-
error_kind: "validation"
|
|
126760
|
-
}
|
|
126761
|
-
);
|
|
126762
|
-
}
|
|
126763
|
-
if (!secrets.some((s) => s.name === name)) secrets.push({ name, value });
|
|
126764
|
-
return value;
|
|
126765
|
-
});
|
|
126766
|
-
return { text: resolved, secrets };
|
|
126767
|
-
}
|
|
126768
|
-
function redactSecretsFromError(err, secrets) {
|
|
126769
|
-
const scrub = (s) => secrets.reduce(
|
|
126770
|
-
(acc, { name, value }) => value ? acc.split(value).join(`${SECRET_PLACEHOLDER_MARKER}${name}}}`) : acc,
|
|
126771
|
-
s
|
|
126772
|
-
);
|
|
126773
|
-
if (err instanceof Error) {
|
|
126774
|
-
err.message = scrub(err.message);
|
|
126775
|
-
if (err.stack) err.stack = scrub(err.stack);
|
|
126776
|
-
return err;
|
|
126777
|
-
}
|
|
126778
|
-
if (typeof err === "string") return scrub(err);
|
|
126779
|
-
return err;
|
|
126780
|
-
}
|
|
126781
|
-
|
|
126782
126677
|
// ../tool-server/src/tools/keyboard/simulator-server-keys.ts
|
|
126783
126678
|
init_src();
|
|
126784
126679
|
|
|
@@ -126889,11 +126784,10 @@ async function typeSimulatorServer(registry2, device, params) {
|
|
|
126889
126784
|
}
|
|
126890
126785
|
keysPressed++;
|
|
126891
126786
|
};
|
|
126892
|
-
let namedKeyCode;
|
|
126893
126787
|
if (params.key) {
|
|
126894
126788
|
const lower = params.key.toLowerCase();
|
|
126895
|
-
|
|
126896
|
-
if (
|
|
126789
|
+
const code = Object.hasOwn(NAMED_KEYS, lower) ? NAMED_KEYS[lower] : void 0;
|
|
126790
|
+
if (code == null) {
|
|
126897
126791
|
throw new InvalidToolInputError(
|
|
126898
126792
|
`Unknown key "${params.key}". Supported: ${Object.keys(NAMED_KEYS).join(", ")}`,
|
|
126899
126793
|
{
|
|
@@ -126903,6 +126797,7 @@ async function typeSimulatorServer(registry2, device, params) {
|
|
|
126903
126797
|
}
|
|
126904
126798
|
);
|
|
126905
126799
|
}
|
|
126800
|
+
await pressKeyCode(code);
|
|
126906
126801
|
}
|
|
126907
126802
|
if (params.text) {
|
|
126908
126803
|
for (const char of params.text) {
|
|
@@ -126917,9 +126812,6 @@ async function typeSimulatorServer(registry2, device, params) {
|
|
|
126917
126812
|
await sleep8(delay2);
|
|
126918
126813
|
}
|
|
126919
126814
|
}
|
|
126920
|
-
if (namedKeyCode != null) {
|
|
126921
|
-
await pressKeyCode(namedKeyCode);
|
|
126922
|
-
}
|
|
126923
126815
|
return { typed: params.text ?? params.key ?? "", keys: keysPressed };
|
|
126924
126816
|
}
|
|
126925
126817
|
|
|
@@ -127063,10 +126955,9 @@ var sleep9 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
|
127063
126955
|
async function runChromium(api, params) {
|
|
127064
126956
|
const delay2 = params.delayMs ?? 50;
|
|
127065
126957
|
let keysPressed = 0;
|
|
127066
|
-
let named;
|
|
127067
126958
|
if (params.key) {
|
|
127068
126959
|
const lower = params.key.toLowerCase();
|
|
127069
|
-
named = Object.hasOwn(CHROMIUM_NAMED_KEYS, lower) ? CHROMIUM_NAMED_KEYS[lower] : void 0;
|
|
126960
|
+
const named = Object.hasOwn(CHROMIUM_NAMED_KEYS, lower) ? CHROMIUM_NAMED_KEYS[lower] : void 0;
|
|
127070
126961
|
if (!named) {
|
|
127071
126962
|
throw new InvalidToolInputError(
|
|
127072
126963
|
`Unknown key "${params.key}". Supported: ${Object.keys(CHROMIUM_NAMED_KEYS).join(", ")}`,
|
|
@@ -127077,6 +126968,20 @@ async function runChromium(api, params) {
|
|
|
127077
126968
|
}
|
|
127078
126969
|
);
|
|
127079
126970
|
}
|
|
126971
|
+
await api.dispatchKeyEvent({
|
|
126972
|
+
type: "keyDown",
|
|
126973
|
+
key: named.key,
|
|
126974
|
+
code: named.code,
|
|
126975
|
+
windowsVirtualKeyCode: named.windowsVirtualKeyCode
|
|
126976
|
+
});
|
|
126977
|
+
await sleep9(delay2);
|
|
126978
|
+
await api.dispatchKeyEvent({
|
|
126979
|
+
type: "keyUp",
|
|
126980
|
+
key: named.key,
|
|
126981
|
+
code: named.code,
|
|
126982
|
+
windowsVirtualKeyCode: named.windowsVirtualKeyCode
|
|
126983
|
+
});
|
|
126984
|
+
keysPressed++;
|
|
127080
126985
|
}
|
|
127081
126986
|
if (params.text) {
|
|
127082
126987
|
for (const char of params.text) {
|
|
@@ -127105,22 +127010,6 @@ async function runChromium(api, params) {
|
|
|
127105
127010
|
await sleep9(delay2);
|
|
127106
127011
|
}
|
|
127107
127012
|
}
|
|
127108
|
-
if (named) {
|
|
127109
|
-
await api.dispatchKeyEvent({
|
|
127110
|
-
type: "keyDown",
|
|
127111
|
-
key: named.key,
|
|
127112
|
-
code: named.code,
|
|
127113
|
-
windowsVirtualKeyCode: named.windowsVirtualKeyCode
|
|
127114
|
-
});
|
|
127115
|
-
await sleep9(delay2);
|
|
127116
|
-
await api.dispatchKeyEvent({
|
|
127117
|
-
type: "keyUp",
|
|
127118
|
-
key: named.key,
|
|
127119
|
-
code: named.code,
|
|
127120
|
-
windowsVirtualKeyCode: named.windowsVirtualKeyCode
|
|
127121
|
-
});
|
|
127122
|
-
keysPressed++;
|
|
127123
|
-
}
|
|
127124
127013
|
return { typed: params.text ?? params.key ?? "", keys: keysPressed };
|
|
127125
127014
|
}
|
|
127126
127015
|
function makeChromiumImpl(registry2) {
|
|
@@ -127204,7 +127093,7 @@ async function injectViaInputd(subcommands) {
|
|
|
127204
127093
|
async function injectVegaButtons(buttons) {
|
|
127205
127094
|
await injectViaInputd(remoteButtonsToKeycodes(buttons).map((code) => `button_press ${code}`));
|
|
127206
127095
|
}
|
|
127207
|
-
function
|
|
127096
|
+
async function injectVegaNamedKey(name) {
|
|
127208
127097
|
const lower = name.toLowerCase();
|
|
127209
127098
|
const code = Object.hasOwn(NAMED_KEYCODES, lower) ? NAMED_KEYCODES[lower] : void 0;
|
|
127210
127099
|
if (!code) {
|
|
@@ -127217,10 +127106,7 @@ function resolveVegaNamedKeycode(name) {
|
|
|
127217
127106
|
}
|
|
127218
127107
|
);
|
|
127219
127108
|
}
|
|
127220
|
-
|
|
127221
|
-
}
|
|
127222
|
-
async function injectVegaNamedKey(name) {
|
|
127223
|
-
await injectViaInputd([`button_press ${resolveVegaNamedKeycode(name)}`]);
|
|
127109
|
+
await injectViaInputd([`button_press ${code}`]);
|
|
127224
127110
|
}
|
|
127225
127111
|
async function injectVegaText(text) {
|
|
127226
127112
|
if (/[\n\r]/.test(text)) {
|
|
@@ -127236,15 +127122,14 @@ async function injectVegaText(text) {
|
|
|
127236
127122
|
// ../tool-server/src/tools/keyboard/platforms/vega.ts
|
|
127237
127123
|
async function runVega2(params) {
|
|
127238
127124
|
let keysPressed = 0;
|
|
127239
|
-
if (params.key) resolveVegaNamedKeycode(params.key);
|
|
127240
|
-
if (params.text) {
|
|
127241
|
-
await injectVegaText(params.text);
|
|
127242
|
-
keysPressed += [...params.text].length;
|
|
127243
|
-
}
|
|
127244
127125
|
if (params.key) {
|
|
127245
127126
|
await injectVegaNamedKey(params.key);
|
|
127246
127127
|
keysPressed++;
|
|
127247
127128
|
}
|
|
127129
|
+
if (params.text) {
|
|
127130
|
+
await injectVegaText(params.text);
|
|
127131
|
+
keysPressed += [...params.text].length;
|
|
127132
|
+
}
|
|
127248
127133
|
return { typed: params.text ?? params.key ?? "", keys: keysPressed };
|
|
127249
127134
|
}
|
|
127250
127135
|
var vegaImpl4 = {
|
|
@@ -127257,11 +127142,9 @@ var zodSchema24 = external_exports.object({
|
|
|
127257
127142
|
udid: external_exports.string().describe(
|
|
127258
127143
|
"Target device id from `list-devices` (iOS UDID, Android serial, Vega serial, or Chromium id)."
|
|
127259
127144
|
),
|
|
127260
|
-
text: external_exports.string().optional().describe(
|
|
127261
|
-
'Text to type character by character. Handles uppercase and common punctuation. To type a credential without its plaintext ever entering your context, use a secret placeholder: `{{secret:<NAME>}}` types the value of the `ARGENT_SECRET_<NAME>` environment variable set on the machine running the tool-server \u2014 e.g. text: "{{secret:APP_PASSWORD}}" types the value of `ARGENT_SECRET_APP_PASSWORD`. Only env vars with the `ARGENT_SECRET_` prefix are resolvable. Placeholders can be embedded in longer text and are never echoed back resolved. If the secret you need is not set, ask the user to export it as `ARGENT_SECRET_<NAME>` and restart the session \u2014 NEVER ask the user to paste the secret value into the conversation.'
|
|
127262
|
-
),
|
|
127145
|
+
text: external_exports.string().optional().describe("Text to type character by character. Handles uppercase and common punctuation."),
|
|
127263
127146
|
key: external_exports.string().optional().describe(
|
|
127264
|
-
"Named key to press: enter, escape, backspace, tab, space, arrow-up, arrow-down, arrow-left, arrow-right, f1\u2013f12.
|
|
127147
|
+
"Named key to press: enter, escape, backspace, tab, space, arrow-up, arrow-down, arrow-left, arrow-right, f1\u2013f12. Not supported on TV targets \u2014 move focus with `tv-remote` (up/down/left/right) instead."
|
|
127265
127148
|
),
|
|
127266
127149
|
delayMs: external_exports.number().optional().describe(
|
|
127267
127150
|
"Delay in ms between key presses (default 50). Ignored on Android phones/tablets (typed via `adb input text`, which has no per-key cadence), on Vega (text/keys injected in a single shot), and on TV targets (Apple TV / Android TV type the whole string at the daemon's own cadence)."
|
|
@@ -127275,24 +127158,15 @@ var capability16 = {
|
|
|
127275
127158
|
vega: { vvd: true }
|
|
127276
127159
|
};
|
|
127277
127160
|
function createKeyboardTool(registry2) {
|
|
127278
|
-
const dispatch = dispatchByPlatform({
|
|
127279
|
-
toolId: "keyboard",
|
|
127280
|
-
capability: capability16,
|
|
127281
|
-
ios: makeIosImpl3(registry2),
|
|
127282
|
-
iosRemote: makeIosRemoteImpl(registry2),
|
|
127283
|
-
android: makeAndroidImpl(registry2),
|
|
127284
|
-
chromium: makeChromiumImpl(registry2),
|
|
127285
|
-
vega: vegaImpl4
|
|
127286
|
-
});
|
|
127287
127161
|
return {
|
|
127288
127162
|
id: "keyboard",
|
|
127289
127163
|
description: `Type text or press special keys on the device (iOS simulator, Android emulator or device, Chromium app, Vega Virtual Device, or Apple TV / Android TV) using keyboard events.
|
|
127290
127164
|
Use when you need to enter text or trigger a named key such as enter, escape, or arrow keys. On Vega and Apple TV / Android TV, prefer the remote tools for D-pad navigation; use keyboard to type into a focused text field (e.g. a search or login box).
|
|
127291
127165
|
Returns { typed: string, keys: number }. Fails if an unsupported key name is provided or the device's input backend is not reachable.
|
|
127292
|
-
- text: types a string (supports uppercase, digits, common punctuation)
|
|
127166
|
+
- text: types a string (supports uppercase, digits, common punctuation)
|
|
127293
127167
|
- key: presses a single named key (enter, escape, backspace, tab, arrow-up/down/left/right, f1\u2013f12) \u2014 NOT supported on TV targets; move focus with \`tv-remote\` instead.
|
|
127294
127168
|
On a TV target (runtimeKind 'tv') only \`text\` applies \u2014 focus a text field first (with \`tv-remote\`), then type into it (injected HID keyboard on Apple TV, \`adb input text\` on Android TV).
|
|
127295
|
-
Provide text, key, or both
|
|
127169
|
+
Provide text, key, or both.`,
|
|
127296
127170
|
zodSchema: zodSchema24,
|
|
127297
127171
|
capability: capability16,
|
|
127298
127172
|
searchHint: "type text keyboard input named key enter escape arrow tv vega fire tv search field hid leanback",
|
|
@@ -127300,17 +127174,15 @@ Provide text, key, or both \u2014 when both are given, the text is typed first a
|
|
|
127300
127174
|
// simulator-server, CDP, or Vega adb), since distinguishing a TV target is
|
|
127301
127175
|
// async and a tvOS udid must never resolve simulator-server.
|
|
127302
127176
|
services: () => ({}),
|
|
127303
|
-
execute:
|
|
127304
|
-
|
|
127305
|
-
|
|
127306
|
-
|
|
127307
|
-
|
|
127308
|
-
|
|
127309
|
-
|
|
127310
|
-
|
|
127311
|
-
|
|
127312
|
-
}
|
|
127313
|
-
}
|
|
127177
|
+
execute: dispatchByPlatform({
|
|
127178
|
+
toolId: "keyboard",
|
|
127179
|
+
capability: capability16,
|
|
127180
|
+
ios: makeIosImpl3(registry2),
|
|
127181
|
+
iosRemote: makeIosRemoteImpl(registry2),
|
|
127182
|
+
android: makeAndroidImpl(registry2),
|
|
127183
|
+
chromium: makeChromiumImpl(registry2),
|
|
127184
|
+
vega: vegaImpl4
|
|
127185
|
+
})
|
|
127314
127186
|
};
|
|
127315
127187
|
}
|
|
127316
127188
|
|
|
@@ -133273,26 +133145,17 @@ async function describeVega(_serial) {
|
|
|
133273
133145
|
|
|
133274
133146
|
// ../tool-server/src/utils/ui-tree-match.ts
|
|
133275
133147
|
init_zod();
|
|
133276
|
-
|
|
133277
|
-
|
|
133278
|
-
}
|
|
133279
|
-
var selectorFieldsSchema = external_exports.object({
|
|
133280
|
-
text: external_exports.string().min(1).refine(hasVisibleText, {
|
|
133281
|
-
message: "text must contain at least one visible character (icon-font/private-use and zero-width characters render as nothing) \u2014 select by identifier or role instead"
|
|
133282
|
-
}).optional().describe("Case-insensitive substring of the element's visible label or value."),
|
|
133148
|
+
var selectorSchema = external_exports.object({
|
|
133149
|
+
text: external_exports.string().min(1).optional().describe("Case-insensitive substring of the element's visible label or value."),
|
|
133283
133150
|
identifier: external_exports.string().min(1).optional().describe(
|
|
133284
133151
|
"The element's identifier (accessibilityIdentifier / resource-id / testid), matched case-insensitively as the exact identifier or the unqualified resource-id name ('submit' matches 'com.example.app:id/submit')."
|
|
133285
133152
|
),
|
|
133286
133153
|
role: external_exports.string().min(1).optional().describe(
|
|
133287
133154
|
"Case-insensitive substring of the element's role (e.g. AXButton, button, TextView)."
|
|
133288
133155
|
)
|
|
133289
|
-
}).
|
|
133290
|
-
|
|
133291
|
-
|
|
133292
|
-
{
|
|
133293
|
-
message: "selector needs at least one of text, identifier, or role"
|
|
133294
|
-
}
|
|
133295
|
-
);
|
|
133156
|
+
}).refine((s) => Boolean(s.text || s.identifier || s.role), {
|
|
133157
|
+
message: "selector needs at least one of text, identifier, or role"
|
|
133158
|
+
});
|
|
133296
133159
|
function nodeText(node) {
|
|
133297
133160
|
return [node.label, node.value].filter(Boolean).join(" ");
|
|
133298
133161
|
}
|
|
@@ -133309,32 +133172,15 @@ function identifierMatches(actual, needle) {
|
|
|
133309
133172
|
if (!actual) return false;
|
|
133310
133173
|
return equalsCI(actual, needle) || actual.toLowerCase().endsWith(`:id/${needle.toLowerCase()}`);
|
|
133311
133174
|
}
|
|
133312
|
-
var uiTreeMatchInternals = {
|
|
133313
|
-
createRegExp(pattern) {
|
|
133314
|
-
return new RegExp(pattern);
|
|
133315
|
-
}
|
|
133316
|
-
};
|
|
133317
|
-
function regexMatchesNonEmpty(regex, actual) {
|
|
133318
|
-
if (!actual) return false;
|
|
133319
|
-
return regex.test(actual);
|
|
133320
|
-
}
|
|
133321
133175
|
function textMatches(actual, expected, mode) {
|
|
133322
|
-
if (mode === "matches") {
|
|
133323
|
-
return regexMatchesNonEmpty(uiTreeMatchInternals.createRegExp(expected), actual);
|
|
133324
|
-
}
|
|
133325
133176
|
return mode === "equals" ? equalsCI(actual, expected) : includesCI(actual, expected);
|
|
133326
133177
|
}
|
|
133327
|
-
function
|
|
133178
|
+
function matchNode(node, selector) {
|
|
133328
133179
|
if (selector.text !== void 0) {
|
|
133329
133180
|
if (!includesCI(node.label, selector.text) && !includesCI(node.value, selector.text)) {
|
|
133330
133181
|
return false;
|
|
133331
133182
|
}
|
|
133332
133183
|
}
|
|
133333
|
-
if (textRegex !== void 0) {
|
|
133334
|
-
if (!regexMatchesNonEmpty(textRegex, node.label) && !regexMatchesNonEmpty(textRegex, node.value)) {
|
|
133335
|
-
return false;
|
|
133336
|
-
}
|
|
133337
|
-
}
|
|
133338
133184
|
if (selector.identifier !== void 0 && !identifierMatches(node.identifier, selector.identifier)) {
|
|
133339
133185
|
return false;
|
|
133340
133186
|
}
|
|
@@ -133343,17 +133189,13 @@ function matchNodeWithRegex(node, selector, textRegex) {
|
|
|
133343
133189
|
}
|
|
133344
133190
|
return true;
|
|
133345
133191
|
}
|
|
133346
|
-
function
|
|
133347
|
-
|
|
133348
|
-
|
|
133349
|
-
function collectMatches(node, selector, textRegex, acc) {
|
|
133350
|
-
if (matchNodeWithRegex(node, selector, textRegex)) acc.push(node);
|
|
133351
|
-
for (const child of node.children) collectMatches(child, selector, textRegex, acc);
|
|
133192
|
+
function collectMatches(node, selector, acc) {
|
|
133193
|
+
if (matchNode(node, selector)) acc.push(node);
|
|
133194
|
+
for (const child of node.children) collectMatches(child, selector, acc);
|
|
133352
133195
|
}
|
|
133353
133196
|
function findAll(root2, selector) {
|
|
133354
133197
|
const acc = [];
|
|
133355
|
-
const
|
|
133356
|
-
for (const child of root2.children) collectMatches(child, selector, textRegex, acc);
|
|
133198
|
+
for (const child of root2.children) collectMatches(child, selector, acc);
|
|
133357
133199
|
return acc;
|
|
133358
133200
|
}
|
|
133359
133201
|
function isVisible(node) {
|
|
@@ -133417,29 +133259,21 @@ function nodeAtPoint(root2, point) {
|
|
|
133417
133259
|
for (const child of root2.children) walk(child);
|
|
133418
133260
|
return best;
|
|
133419
133261
|
}
|
|
133420
|
-
function
|
|
133421
|
-
return selector.textMatches === void 0 ? void 0 : uiTreeMatchInternals.createRegExp(`^(?:${selector.textMatches})$`);
|
|
133422
|
-
}
|
|
133423
|
-
function exactFieldCount(node, selector, fullTextRegex) {
|
|
133262
|
+
function exactFieldCount(node, selector) {
|
|
133424
133263
|
let count = 0;
|
|
133425
133264
|
if (selector.text !== void 0 && (equalsCI(node.label, selector.text) || equalsCI(node.value, selector.text))) {
|
|
133426
133265
|
count++;
|
|
133427
133266
|
}
|
|
133428
|
-
if (fullTextRegex !== void 0 && (regexMatchesNonEmpty(fullTextRegex, node.label) || regexMatchesNonEmpty(fullTextRegex, node.value))) {
|
|
133429
|
-
count++;
|
|
133430
|
-
}
|
|
133431
133267
|
if (selector.identifier !== void 0 && equalsCI(node.identifier, selector.identifier)) count++;
|
|
133432
133268
|
if (selector.role !== void 0 && equalsCI(node.role, selector.role)) count++;
|
|
133433
133269
|
return count;
|
|
133434
133270
|
}
|
|
133435
133271
|
function selectorToFrame(root2, selector) {
|
|
133436
133272
|
const visible = findAll(root2, selector).filter(isVisible);
|
|
133437
|
-
if (visible.length === 0) return void 0;
|
|
133438
|
-
const fullTextRegex = fullConsumptionRegex(selector);
|
|
133439
133273
|
let best;
|
|
133440
133274
|
let bestExact = -1;
|
|
133441
133275
|
for (const n of visible) {
|
|
133442
|
-
const exact = exactFieldCount(n, selector
|
|
133276
|
+
const exact = exactFieldCount(n, selector);
|
|
133443
133277
|
if (best === void 0 || exact !== bestExact) {
|
|
133444
133278
|
if (exact > bestExact) {
|
|
133445
133279
|
best = n;
|
|
@@ -133467,7 +133301,7 @@ var GENERIC_ROLES = /* @__PURE__ */ new Set([
|
|
|
133467
133301
|
]);
|
|
133468
133302
|
function deriveSelector(node) {
|
|
133469
133303
|
if (node.identifier && node.identifier.trim()) return { identifier: node.identifier };
|
|
133470
|
-
const text = [node.label, node.value].map((t) => t?.trim()).find(
|
|
133304
|
+
const text = [node.label, node.value].map((t) => t?.trim()).find(Boolean);
|
|
133471
133305
|
if (text) return { text };
|
|
133472
133306
|
if (node.role && !GENERIC_ROLES.has(node.role.toLowerCase())) return { role: node.role };
|
|
133473
133307
|
return null;
|
|
@@ -133723,7 +133557,7 @@ a prior tap), use individual tool calls instead.
|
|
|
133723
133557
|
|
|
133724
133558
|
Allowed tools and their args (udid is auto-injected, do NOT include it in args):
|
|
133725
133559
|
|
|
133726
|
-
gesture-tap: { x: number, y: number
|
|
133560
|
+
gesture-tap: { x: number, y: number } [ios/android/chromium]
|
|
133727
133561
|
gesture-swipe: { fromX: number, fromY: number, toX: number, toY: number, durationMs?: number } [ios/android]
|
|
133728
133562
|
gesture-scroll: { x: number, y: number, deltaX?: number, deltaY?: number, durationMs?: number } [chromium only]
|
|
133729
133563
|
gesture-drag: { fromX: number, fromY: number, toX: number, toY: number, durationMs?: number } [chromium only]
|
|
@@ -133731,8 +133565,7 @@ Allowed tools and their args (udid is auto-injected, do NOT include it in args):
|
|
|
133731
133565
|
gesture-pinch: { centerX: number, centerY: number, startDistance: number, endDistance: number, angle?: number, durationMs?: number } [ios only]
|
|
133732
133566
|
gesture-rotate: { centerX: number, centerY: number, radius: number, startAngle: number, endAngle: number, durationMs?: number } [ios only]
|
|
133733
133567
|
button: { button: "home"|"back"|"power"|"volumeUp"|"volumeDown"|"appSwitch"|"actionButton" } [ios/android]
|
|
133734
|
-
keyboard: { text?: string, key?: string, delayMs?: number } (
|
|
133735
|
-
text supports {{secret:<NAME>}} placeholders, resolved server-side from ARGENT_SECRET_<NAME> env vars (prefix mandatory) \u2014 credentials never enter agent context
|
|
133568
|
+
keyboard: { text?: string, key?: string, delayMs?: number } (TV: text only) [ios/android/chromium/vega/tv]
|
|
133736
133569
|
rotate: { orientation: "Portrait"|"LandscapeLeft"|"LandscapeRight"|"PortraitUpsideDown" } [ios/android]
|
|
133737
133570
|
tv-remote: { button: <remote button | array of them>, repeat?: number } [apple tv/android tv/vega]
|
|
133738
133571
|
buttons: up/down/left/right/select/back/home/menu/playPause (+ rewind/fastForward/next/previous/volumeUp/volumeDown/mute \u2014 work on Android TV and Vega; rejected on the Apple TV simulator)
|
|
@@ -133745,9 +133578,10 @@ Example \u2014 scroll down three times (use gesture-scroll with positive deltaY
|
|
|
133745
133578
|
{ "tool": "gesture-swipe", "args": { "fromX": 0.5, "fromY": 0.7, "toX": 0.5, "toY": 0.3 } }
|
|
133746
133579
|
]}
|
|
133747
133580
|
|
|
133748
|
-
Example \u2014 type text and submit
|
|
133581
|
+
Example \u2014 type text and submit:
|
|
133749
133582
|
{ "udid": "<UDID>", "steps": [
|
|
133750
|
-
{ "tool": "keyboard", "args": { "text": "hello world"
|
|
133583
|
+
{ "tool": "keyboard", "args": { "text": "hello world" } },
|
|
133584
|
+
{ "tool": "keyboard", "args": { "key": "enter" } }
|
|
133751
133585
|
]}
|
|
133752
133586
|
|
|
133753
133587
|
Example \u2014 TV: move focus right twice then activate (one tv-remote step with a path is cheaper):
|
|
@@ -133845,8 +133679,7 @@ var DEBUGGER_TOOL_CAPABILITY = {
|
|
|
133845
133679
|
apple: { simulator: true, device: true },
|
|
133846
133680
|
appleRemote: { simulator: true },
|
|
133847
133681
|
android: { emulator: true, device: true, unknown: true },
|
|
133848
|
-
chromium: { app: true }
|
|
133849
|
-
vega: { vvd: true }
|
|
133682
|
+
chromium: { app: true }
|
|
133850
133683
|
};
|
|
133851
133684
|
var RN_ONLY_TOOL_CAPABILITY = {
|
|
133852
133685
|
apple: { simulator: true, device: true },
|
|
@@ -133865,14 +133698,14 @@ function debuggerServiceRef(params) {
|
|
|
133865
133698
|
var zodSchema29 = external_exports.object({
|
|
133866
133699
|
port: external_exports.coerce.number().default(8081).describe("Metro server port (ignored for Chromium \u2014 its CDP port is encoded in device_id)"),
|
|
133867
133700
|
device_id: external_exports.string().describe(
|
|
133868
|
-
"Device id: iOS simulator UDID, Android logicalDeviceId returned by Metro,
|
|
133701
|
+
"Device id: iOS simulator UDID, Android logicalDeviceId returned by Metro, or Chromium device id (chromium-cdp-<port>) from list-devices. The returned logicalDeviceId must be forwarded as device_id to all subsequent debugger-* calls to pin them to this device."
|
|
133869
133702
|
)
|
|
133870
133703
|
});
|
|
133871
133704
|
var debuggerConnectTool = {
|
|
133872
133705
|
id: "debugger-connect",
|
|
133873
133706
|
description: `Connect to a JS runtime CDP debugger.
|
|
133874
|
-
iOS / Android
|
|
133875
|
-
Returns connection info including port, projectRoot (empty on Chromium
|
|
133707
|
+
iOS / Android: connects to Metro's CDP endpoint on the given port. Chromium: re-uses the page CDP session opened by boot-device \u2014 port is ignored.
|
|
133708
|
+
Returns connection info including port, projectRoot (empty on Chromium), deviceName, appName, logicalDeviceId, and isNewDebugger. If already connected, returns the existing connection.
|
|
133876
133709
|
Use when starting a debug session or before calling other debugger-* tools. Fails if the runtime is unreachable (Metro down, or Chromium CDP terminated).`,
|
|
133877
133710
|
zodSchema: zodSchema29,
|
|
133878
133711
|
capability: DEBUGGER_TOOL_CAPABILITY,
|
|
@@ -133898,13 +133731,13 @@ init_zod();
|
|
|
133898
133731
|
var zodSchema30 = external_exports.object({
|
|
133899
133732
|
port: external_exports.coerce.number().default(8081).describe("Metro server port (ignored for Chromium)"),
|
|
133900
133733
|
device_id: external_exports.string().describe(
|
|
133901
|
-
"Device id from debugger-connect (iOS simulator UDID, Android logicalDeviceId,
|
|
133734
|
+
"Device id from debugger-connect (iOS simulator UDID, Android logicalDeviceId, or Chromium device id)."
|
|
133902
133735
|
)
|
|
133903
133736
|
});
|
|
133904
133737
|
var debuggerStatusTool = {
|
|
133905
133738
|
id: "debugger-status",
|
|
133906
133739
|
description: `Get JS runtime debugger connection status and diagnostic info.
|
|
133907
|
-
Use when you need to verify connectivity before using other debugger tools. Returns port, projectRoot (empty on Chromium
|
|
133740
|
+
Use when you need to verify connectivity before using other debugger tools. Returns port, projectRoot (empty on Chromium), deviceName, appName, logicalDeviceId, connected flag, loadedScripts count, and sourceMapReady (always true \u2014 waits for pending source maps before returning; no-op on Chromium). Fails if the runtime is unreachable.`,
|
|
133908
133741
|
zodSchema: zodSchema30,
|
|
133909
133742
|
capability: DEBUGGER_TOOL_CAPABILITY,
|
|
133910
133743
|
services: (params) => ({
|
|
@@ -133933,13 +133766,13 @@ init_zod();
|
|
|
133933
133766
|
var zodSchema31 = external_exports.object({
|
|
133934
133767
|
port: external_exports.coerce.number().default(8081).describe("Metro server port (ignored for Chromium)"),
|
|
133935
133768
|
device_id: external_exports.string().describe(
|
|
133936
|
-
"Device id from debugger-connect (iOS simulator UDID, Android logicalDeviceId,
|
|
133769
|
+
"Device id from debugger-connect (iOS simulator UDID, Android logicalDeviceId, or Chromium device id)."
|
|
133937
133770
|
),
|
|
133938
133771
|
expression: external_exports.string().describe("JavaScript expression to evaluate in the app runtime")
|
|
133939
133772
|
});
|
|
133940
133773
|
var debuggerEvaluateTool = {
|
|
133941
133774
|
id: "debugger-evaluate",
|
|
133942
|
-
description: `Execute arbitrary JavaScript in the app's JS runtime via CDP \u2014 Hermes on iOS / Android
|
|
133775
|
+
description: `Execute arbitrary JavaScript in the app's JS runtime via CDP \u2014 Hermes on iOS / Android, V8 on Chromium.
|
|
133943
133776
|
Returns the evaluation result as a JSON-serializable value, along with deviceName, appName, and logicalDeviceId for context. Use when you need to read app state, call app functions, or test logic at runtime. The result is serialized by value, so cyclic objects (many RN runtime values \u2014 fiber nodes, navigation refs, global \u2014 are cyclic) fail with a serialization error rather than returning silently. Fails if the expression throws or the runtime is not connected.`,
|
|
133944
133777
|
zodSchema: zodSchema31,
|
|
133945
133778
|
capability: DEBUGGER_TOOL_CAPABILITY,
|
|
@@ -135423,13 +135256,13 @@ init_zod();
|
|
|
135423
135256
|
var zodSchema35 = external_exports.object({
|
|
135424
135257
|
port: external_exports.coerce.number().default(8081).describe("Metro server port (ignored for Chromium)"),
|
|
135425
135258
|
device_id: external_exports.string().describe(
|
|
135426
|
-
"Device id from debugger-connect (iOS simulator UDID, Android logicalDeviceId,
|
|
135259
|
+
"Device id from debugger-connect (iOS simulator UDID, Android logicalDeviceId, or Chromium device id)."
|
|
135427
135260
|
)
|
|
135428
135261
|
});
|
|
135429
135262
|
var debuggerLogRegistryTool = {
|
|
135430
135263
|
id: "debugger-log-registry",
|
|
135431
135264
|
description: `Get a summary of all console logs captured from the app's JS runtime.
|
|
135432
|
-
Returns the log file path, entry counts by level, and message clusters (grouped by similarity). Works against Hermes (iOS / Android
|
|
135265
|
+
Returns the log file path, entry counts by level, and message clusters (grouped by similarity). Works against Hermes (iOS / Android) and V8 (Chromium).
|
|
135433
135266
|
Use when investigating warnings, errors, or unexpected output \u2014 call this first for an overview, then read the returned file for details. Returns empty stats if no log data has been captured yet.`,
|
|
135434
135267
|
zodSchema: zodSchema35,
|
|
135435
135268
|
capability: DEBUGGER_TOOL_CAPABILITY,
|
|
@@ -135521,7 +135354,7 @@ var networkLogsTool = {
|
|
|
135521
135354
|
description: `Retrieve captured network (HTTP) requests from the running app.
|
|
135522
135355
|
Returns a paginated list of requests with method, URL, status, resource type, size, and duration.
|
|
135523
135356
|
Each entry includes a requestId that can be passed to view-network-request-details for full details.
|
|
135524
|
-
On React Native (iOS
|
|
135357
|
+
On React Native (iOS/Android) interception is injected into the JS runtime \u2014 it captures fetch() calls. On Chromium it reads the browser's native CDP Network domain (the active tab; all request types).
|
|
135525
135358
|
Use when inspecting outbound HTTP traffic or debugging API calls in the running app.
|
|
135526
135359
|
Fails if the app is not connected (RN) or the device is not reachable (Chromium).`,
|
|
135527
135360
|
zodSchema: zodSchema36,
|
|
@@ -144664,67 +144497,14 @@ function chromiumLaunchSpec(launch) {
|
|
|
144664
144497
|
return typeof c === "string" ? { path: c } : { path: c.path, args: c.args };
|
|
144665
144498
|
}
|
|
144666
144499
|
function selectorToYaml(sel) {
|
|
144667
|
-
if (sel.text !== void 0 && sel.textMatches !== void 0) {
|
|
144668
|
-
throw new Error(
|
|
144669
|
-
'Cannot serialize flow selector without losing constraints: both `text` and `textMatches` are set, but flow YAML can represent only one `text` constraint (a literal string or `{ matches: "<regex>" }`). Use either literal or regex text matching.'
|
|
144670
|
-
);
|
|
144671
|
-
}
|
|
144672
|
-
if (sel.text !== void 0 && (typeof sel.text !== "string" || !hasVisibleText(sel.text))) {
|
|
144673
|
-
throw new Error(
|
|
144674
|
-
"Cannot serialize flow selector: `text` must contain at least one visible character (icon-font/private-use and zero-width characters render as nothing). Select by identifier or role, or use a coordinate tap."
|
|
144675
|
-
);
|
|
144676
|
-
}
|
|
144677
|
-
if (sel.loose && (sel.text === void 0 || sel.textMatches !== void 0 || sel.identifier !== void 0 || sel.role !== void 0)) {
|
|
144678
|
-
const incompatible = [
|
|
144679
|
-
sel.textMatches !== void 0 ? "textMatches" : void 0,
|
|
144680
|
-
sel.identifier !== void 0 ? "identifier" : void 0,
|
|
144681
|
-
sel.role !== void 0 ? "role" : void 0
|
|
144682
|
-
].filter((field) => field !== void 0);
|
|
144683
|
-
throw new Error(
|
|
144684
|
-
"Cannot serialize loose flow selector without changing its meaning: bare-string YAML can represent only a loose text-only selector" + (incompatible.length > 0 ? `; incompatible fields: ${incompatible.join(", ")}` : "") + "."
|
|
144685
|
-
);
|
|
144686
|
-
}
|
|
144687
144500
|
if (sel.loose && sel.text !== void 0 && sel.identifier === void 0 && sel.role === void 0) {
|
|
144688
144501
|
return sel.text;
|
|
144689
144502
|
}
|
|
144690
|
-
const { loose: _loose, identifier,
|
|
144691
|
-
|
|
144692
|
-
if (textMatches2 !== void 0) out.text = { matches: textMatches2 };
|
|
144693
|
-
if (identifier !== void 0) out.id = identifier;
|
|
144694
|
-
return out;
|
|
144503
|
+
const { loose: _loose, identifier, ...rest } = sel;
|
|
144504
|
+
return identifier === void 0 ? { ...rest } : { ...rest, id: identifier };
|
|
144695
144505
|
}
|
|
144696
144506
|
function describeSelector(s) {
|
|
144697
|
-
return Object.entries(s).filter(([k]) => k !== "loose").map(
|
|
144698
|
-
([k, v]) => k === "textMatches" ? `text=/${v}/` : `${k === "identifier" ? "id" : k}="${v}"`
|
|
144699
|
-
).join(" ");
|
|
144700
|
-
}
|
|
144701
|
-
function describeTextExpectation(expectedText, textMatch, verbForm = "mode") {
|
|
144702
|
-
const expected = expectedText ?? "";
|
|
144703
|
-
const mode = textMatch ?? "contains";
|
|
144704
|
-
switch (mode) {
|
|
144705
|
-
case "contains":
|
|
144706
|
-
return `${verbForm === "infinitive" ? "contain" : mode} ${JSON.stringify(expected)}`;
|
|
144707
|
-
case "equals":
|
|
144708
|
-
return `${verbForm === "infinitive" ? "equal" : mode} ${JSON.stringify(expected)}`;
|
|
144709
|
-
case "matches":
|
|
144710
|
-
return `${verbForm === "infinitive" ? "match" : mode} /${expected}/`;
|
|
144711
|
-
}
|
|
144712
|
-
}
|
|
144713
|
-
function textWaitToYaml(selector, expectedText, textMatch) {
|
|
144714
|
-
const expected = expectedText ?? "";
|
|
144715
|
-
const mode = textMatch ?? "contains";
|
|
144716
|
-
switch (mode) {
|
|
144717
|
-
case "contains":
|
|
144718
|
-
return { text: { in: selector, contains: expected } };
|
|
144719
|
-
case "equals":
|
|
144720
|
-
return { text: { in: selector, equals: expected } };
|
|
144721
|
-
case "matches":
|
|
144722
|
-
return { text: { in: selector, matches: expected } };
|
|
144723
|
-
default: {
|
|
144724
|
-
const exhaustive = mode;
|
|
144725
|
-
throw new Error(`Unsupported text match mode: ${exhaustive}`);
|
|
144726
|
-
}
|
|
144727
|
-
}
|
|
144507
|
+
return Object.entries(s).filter(([k]) => k !== "loose").map(([k, v]) => `${k === "identifier" ? "id" : k}="${v}"`).join(" ");
|
|
144728
144508
|
}
|
|
144729
144509
|
function waitToYaml(condition, selector, expectedText, textMatch, timeoutMs) {
|
|
144730
144510
|
const sel = selectorToYaml(selector);
|
|
@@ -144740,7 +144520,7 @@ function waitToYaml(condition, selector, expectedText, textMatch, timeoutMs) {
|
|
|
144740
144520
|
body = { hidden: sel };
|
|
144741
144521
|
break;
|
|
144742
144522
|
case "text":
|
|
144743
|
-
body =
|
|
144523
|
+
body = textMatch === "equals" ? { text: { in: sel, equals: expectedText ?? "" } } : { text: { in: sel, contains: expectedText ?? "" } };
|
|
144744
144524
|
break;
|
|
144745
144525
|
}
|
|
144746
144526
|
if (timeoutMs !== void 0) body.timeout = timeoutMs;
|
|
@@ -144755,20 +144535,9 @@ function toYamlStep(step) {
|
|
|
144755
144535
|
case "run":
|
|
144756
144536
|
return { run: step.flow };
|
|
144757
144537
|
case "tap": {
|
|
144758
|
-
|
|
144759
|
-
const sel = selectorToYaml(step.selector);
|
|
144760
|
-
return { tap: step.times !== void 0 ? { on: sel, times: step.times } : sel };
|
|
144761
|
-
}
|
|
144762
|
-
const body = { x: step.x, y: step.y };
|
|
144763
|
-
if (step.times !== void 0) body.times = step.times;
|
|
144538
|
+
const body = step.selector ? selectorToYaml(step.selector) : { x: step.x, y: step.y };
|
|
144764
144539
|
return { tap: body };
|
|
144765
144540
|
}
|
|
144766
|
-
case "long-press": {
|
|
144767
|
-
const sel = selectorToYaml(step.selector);
|
|
144768
|
-
return {
|
|
144769
|
-
"long-press": step.duration !== void 0 ? { on: sel, duration: step.duration } : sel
|
|
144770
|
-
};
|
|
144771
|
-
}
|
|
144772
144541
|
case "type": {
|
|
144773
144542
|
const body = {
|
|
144774
144543
|
into: selectorToYaml(step.into),
|
|
@@ -144833,16 +144602,6 @@ function badEntry(raw, detail) {
|
|
|
144833
144602
|
error_kind: "validation"
|
|
144834
144603
|
});
|
|
144835
144604
|
}
|
|
144836
|
-
function validatePattern(raw, pattern, where) {
|
|
144837
|
-
try {
|
|
144838
|
-
new RegExp(pattern);
|
|
144839
|
-
} catch (err) {
|
|
144840
|
-
badEntry(
|
|
144841
|
-
raw,
|
|
144842
|
-
`${where} \`matches\` is not a valid regular expression: ${err instanceof Error ? err.message : String(err)}`
|
|
144843
|
-
);
|
|
144844
|
-
}
|
|
144845
|
-
}
|
|
144846
144605
|
function editDistance(a, b) {
|
|
144847
144606
|
let prevPrev = new Array(b.length + 1);
|
|
144848
144607
|
let prev = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
@@ -144906,46 +144665,11 @@ function parseSelector(raw, where) {
|
|
|
144906
144665
|
}
|
|
144907
144666
|
normalized = { ...rest, identifier: id };
|
|
144908
144667
|
}
|
|
144909
|
-
if (normalized !== null && typeof normalized === "object") {
|
|
144910
|
-
const { text, ...rest } = normalized;
|
|
144911
|
-
if (text !== null && typeof text === "object") {
|
|
144912
|
-
const keys = Object.keys(text);
|
|
144913
|
-
if (!Array.isArray(text)) {
|
|
144914
|
-
rejectUnknownKeys(
|
|
144915
|
-
raw,
|
|
144916
|
-
text,
|
|
144917
|
-
["matches"],
|
|
144918
|
-
`${where}: text matcher`
|
|
144919
|
-
);
|
|
144920
|
-
}
|
|
144921
|
-
const pattern = text.matches;
|
|
144922
|
-
if (keys.length !== 1 || keys[0] !== "matches") {
|
|
144923
|
-
badEntry(
|
|
144924
|
-
raw,
|
|
144925
|
-
`${where}: a text matcher takes exactly { matches: '<regex>' } \u2014 for a substring, use the plain-string form (text: "\u2026")`
|
|
144926
|
-
);
|
|
144927
|
-
}
|
|
144928
|
-
if (typeof pattern !== "string" || pattern.length === 0) {
|
|
144929
|
-
badEntry(raw, `${where}: text matcher needs a non-empty \`matches\` pattern`);
|
|
144930
|
-
}
|
|
144931
|
-
validatePattern(raw, pattern, `${where}: text`);
|
|
144932
|
-
const fields = selectorFieldsSchema.safeParse(rest);
|
|
144933
|
-
if (!fields.success) {
|
|
144934
|
-
badEntry(raw, `${where}: ${fields.error.issues[0]?.message ?? "invalid selector"}`);
|
|
144935
|
-
}
|
|
144936
|
-
return { ...fields.data, textMatches: pattern };
|
|
144937
|
-
}
|
|
144938
|
-
}
|
|
144939
144668
|
const r = selectorSchema.safeParse(normalized);
|
|
144940
144669
|
if (!r.success) badEntry(raw, `${where}: ${r.error.issues[0]?.message ?? "invalid selector"}`);
|
|
144941
144670
|
return r.data;
|
|
144942
144671
|
}
|
|
144943
144672
|
var WAIT_CONDITIONS = ["exists", "visible", "hidden", "text"];
|
|
144944
|
-
var TEXT_MATCH_MODES = Object.keys({
|
|
144945
|
-
contains: true,
|
|
144946
|
-
equals: true,
|
|
144947
|
-
matches: true
|
|
144948
|
-
});
|
|
144949
144673
|
var SCROLL_DIRECTIONS = ["up", "down", "left", "right"];
|
|
144950
144674
|
function parseWaitFields(raw, kind) {
|
|
144951
144675
|
if (raw === null || typeof raw !== "object") {
|
|
@@ -144985,30 +144709,22 @@ function parseWaitFields(raw, kind) {
|
|
|
144985
144709
|
if (condition === "text") {
|
|
144986
144710
|
const t = b.text;
|
|
144987
144711
|
if (t === null || typeof t !== "object") {
|
|
144988
|
-
badEntry(
|
|
144989
|
-
{ [kind]: b },
|
|
144990
|
-
`${kind} text needs { in: <selector>, contains|equals|matches: <string> }`
|
|
144991
|
-
);
|
|
144712
|
+
badEntry({ [kind]: b }, `${kind} text needs { in: <selector>, contains|equals: <string> }`);
|
|
144992
144713
|
}
|
|
144993
144714
|
const tb = t;
|
|
144994
144715
|
if (!Array.isArray(tb)) {
|
|
144995
|
-
rejectUnknownKeys({ [kind]: b }, tb, ["in",
|
|
144716
|
+
rejectUnknownKeys({ [kind]: b }, tb, ["in", "contains", "equals"], `${kind}.text`);
|
|
144996
144717
|
}
|
|
144997
|
-
const
|
|
144998
|
-
|
|
144999
|
-
|
|
145000
|
-
|
|
145001
|
-
`${kind} text needs exactly one of \`contains\`, \`equals\`, or \`matches\``
|
|
145002
|
-
);
|
|
144718
|
+
const hasContains = "contains" in tb;
|
|
144719
|
+
const hasEquals = "equals" in tb;
|
|
144720
|
+
if (hasContains === hasEquals) {
|
|
144721
|
+
badEntry({ [kind]: b }, `${kind} text needs exactly one of \`contains\` or \`equals\``);
|
|
145003
144722
|
}
|
|
145004
|
-
const textMatch =
|
|
145005
|
-
const expected = tb
|
|
144723
|
+
const textMatch = hasEquals ? "equals" : "contains";
|
|
144724
|
+
const expected = hasEquals ? tb.equals : tb.contains;
|
|
145006
144725
|
if (typeof expected !== "string" || expected.length === 0) {
|
|
145007
144726
|
badEntry({ [kind]: b }, `${kind} text needs a non-empty \`${textMatch}\``);
|
|
145008
144727
|
}
|
|
145009
|
-
if (textMatch === "matches") {
|
|
145010
|
-
validatePattern({ [kind]: b }, expected, `${kind} text`);
|
|
145011
|
-
}
|
|
145012
144728
|
return {
|
|
145013
144729
|
condition: "text",
|
|
145014
144730
|
selector: parseSelector(tb.in, `${kind}.text.in`),
|
|
@@ -145071,7 +144787,6 @@ var STEP_DIRECTIVE_KEYS = [
|
|
|
145071
144787
|
"run",
|
|
145072
144788
|
"tool",
|
|
145073
144789
|
"tap",
|
|
145074
|
-
"long-press",
|
|
145075
144790
|
"type",
|
|
145076
144791
|
"await",
|
|
145077
144792
|
"assert",
|
|
@@ -145079,79 +144794,6 @@ var STEP_DIRECTIVE_KEYS = [
|
|
|
145079
144794
|
"scroll-to",
|
|
145080
144795
|
"snapshot"
|
|
145081
144796
|
];
|
|
145082
|
-
function parseLongPress(body, entry) {
|
|
145083
|
-
const obj = body !== null && typeof body === "object" ? body : {};
|
|
145084
|
-
if (obj.on !== void 0 || obj.duration !== void 0) {
|
|
145085
|
-
if (obj.text !== void 0 || obj.id !== void 0 || obj.identifier !== void 0 || obj.role !== void 0) {
|
|
145086
|
-
badEntry(
|
|
145087
|
-
entry,
|
|
145088
|
-
'the long-press options form takes a nested selector \u2014 e.g. long-press: { on: { text: "Row" }, duration: 1200 }'
|
|
145089
|
-
);
|
|
145090
|
-
}
|
|
145091
|
-
if (!Object.keys(obj).every((k) => k === "on" || k === "duration")) {
|
|
145092
|
-
badEntry(entry, "the long-press options form accepts only { on, duration }");
|
|
145093
|
-
}
|
|
145094
|
-
if (obj.on === void 0) {
|
|
145095
|
-
badEntry(entry, 'long-press needs a target \u2014 e.g. long-press: { on: "Row", duration: 1200 }');
|
|
145096
|
-
}
|
|
145097
|
-
const step = { kind: "long-press", selector: parseSelector(obj.on, "long-press.on") };
|
|
145098
|
-
if (obj.duration !== void 0) {
|
|
145099
|
-
if (typeof obj.duration !== "number" || !Number.isFinite(obj.duration) || obj.duration <= 0) {
|
|
145100
|
-
badEntry(
|
|
145101
|
-
entry,
|
|
145102
|
-
"long-press.duration needs a positive number of milliseconds (e.g. `duration: 1200`)"
|
|
145103
|
-
);
|
|
145104
|
-
}
|
|
145105
|
-
step.duration = obj.duration;
|
|
145106
|
-
}
|
|
145107
|
-
return step;
|
|
145108
|
-
}
|
|
145109
|
-
return { kind: "long-press", selector: parseSelector(body, "long-press") };
|
|
145110
|
-
}
|
|
145111
|
-
function parseTapTimes(raw, entry) {
|
|
145112
|
-
if (raw === void 0) return void 0;
|
|
145113
|
-
if (typeof raw !== "number" || !Number.isInteger(raw) || raw < 1 || raw > 10) {
|
|
145114
|
-
badEntry(entry, "tap.times must be an integer between 1 and 10 (2 = double-tap)");
|
|
145115
|
-
}
|
|
145116
|
-
return raw === 1 ? void 0 : raw;
|
|
145117
|
-
}
|
|
145118
|
-
function parseTap(body, entry) {
|
|
145119
|
-
const obj = body !== null && typeof body === "object" ? body : {};
|
|
145120
|
-
if (obj.x !== void 0 || obj.y !== void 0) {
|
|
145121
|
-
if (obj.on !== void 0 || obj.text !== void 0 || obj.id !== void 0 || obj.identifier !== void 0 || obj.role !== void 0) {
|
|
145122
|
-
badEntry(entry, "tap takes a selector or x/y coordinates, not both");
|
|
145123
|
-
}
|
|
145124
|
-
if (typeof obj.x !== "number" || typeof obj.y !== "number") {
|
|
145125
|
-
badEntry(entry, "a coordinate tap needs numeric x and y");
|
|
145126
|
-
}
|
|
145127
|
-
if (!Object.keys(obj).every((k) => k === "x" || k === "y" || k === "times")) {
|
|
145128
|
-
badEntry(entry, "a coordinate tap takes only { x, y, times }");
|
|
145129
|
-
}
|
|
145130
|
-
const step = { kind: "tap", x: obj.x, y: obj.y };
|
|
145131
|
-
const times = parseTapTimes(obj.times, entry);
|
|
145132
|
-
if (times !== void 0) step.times = times;
|
|
145133
|
-
return step;
|
|
145134
|
-
}
|
|
145135
|
-
if (obj.on !== void 0 || obj.times !== void 0) {
|
|
145136
|
-
if (obj.text !== void 0 || obj.id !== void 0 || obj.identifier !== void 0 || obj.role !== void 0) {
|
|
145137
|
-
badEntry(
|
|
145138
|
-
entry,
|
|
145139
|
-
'the tap options form takes a nested selector \u2014 e.g. tap: { on: { text: "Photo" }, times: 2 }'
|
|
145140
|
-
);
|
|
145141
|
-
}
|
|
145142
|
-
if (!Object.keys(obj).every((k) => k === "on" || k === "times")) {
|
|
145143
|
-
badEntry(entry, "the tap options form accepts only { on, times }");
|
|
145144
|
-
}
|
|
145145
|
-
if (obj.on === void 0) {
|
|
145146
|
-
badEntry(entry, 'tap with times needs a target \u2014 e.g. tap: { on: "Photo", times: 2 }');
|
|
145147
|
-
}
|
|
145148
|
-
const step = { kind: "tap", selector: parseSelector(obj.on, "tap.on") };
|
|
145149
|
-
const times = parseTapTimes(obj.times, entry);
|
|
145150
|
-
if (times !== void 0) step.times = times;
|
|
145151
|
-
return step;
|
|
145152
|
-
}
|
|
145153
|
-
return { kind: "tap", selector: parseSelector(body, "tap") };
|
|
145154
|
-
}
|
|
145155
144797
|
function fromYamlStep(raw) {
|
|
145156
144798
|
const entry = raw;
|
|
145157
144799
|
const kinds = STEP_DIRECTIVE_KEYS.filter((k) => k in entry);
|
|
@@ -145177,9 +144819,22 @@ function fromYamlStep(raw) {
|
|
|
145177
144819
|
if ("echo" in raw) return { kind: "echo", message: String(raw.echo) };
|
|
145178
144820
|
if ("launch" in raw) return { kind: "launch", app: parseLaunch(raw.launch) };
|
|
145179
144821
|
if ("run" in raw) return { kind: "run", flow: String(raw.run) };
|
|
145180
|
-
if ("tap" in raw)
|
|
145181
|
-
|
|
145182
|
-
|
|
144822
|
+
if ("tap" in raw) {
|
|
144823
|
+
const body = raw.tap;
|
|
144824
|
+
const obj = body !== null && typeof body === "object" ? body : {};
|
|
144825
|
+
if (body !== null && typeof body === "object" && !Array.isArray(body)) {
|
|
144826
|
+
rejectUnknownKeys(raw, obj, [...SELECTOR_KEYS, "x", "y"], "tap");
|
|
144827
|
+
}
|
|
144828
|
+
if (obj.x !== void 0 || obj.y !== void 0) {
|
|
144829
|
+
if (obj.text !== void 0 || obj.id !== void 0 || obj.identifier !== void 0 || obj.role !== void 0) {
|
|
144830
|
+
badEntry(raw, "tap takes a selector or x/y coordinates, not both");
|
|
144831
|
+
}
|
|
144832
|
+
if (typeof obj.x !== "number" || typeof obj.y !== "number") {
|
|
144833
|
+
badEntry(raw, "a coordinate tap needs numeric x and y");
|
|
144834
|
+
}
|
|
144835
|
+
return { kind: "tap", x: obj.x, y: obj.y };
|
|
144836
|
+
}
|
|
144837
|
+
return { kind: "tap", selector: parseSelector(body, "tap") };
|
|
145183
144838
|
}
|
|
145184
144839
|
if ("type" in raw) {
|
|
145185
144840
|
const body = raw.type;
|
|
@@ -146016,15 +145671,13 @@ If a step was recorded by mistake, edit the .yaml file directly to remove it.`,
|
|
|
146016
145671
|
const runTarget = params.command === RUN_TARGET_COMMAND && params.delayMs === void 0 ? await captureRunTarget(session, args) : void 0;
|
|
146017
145672
|
const strippedArgs = stripDeviceKeys(args);
|
|
146018
145673
|
const isLaunch = params.command === "restart-app" && params.delayMs === void 0 && typeof strippedArgs.bundleId === "string" && Object.keys(strippedArgs).length === 1;
|
|
146019
|
-
const cc = args.clickCount;
|
|
146020
|
-
const tapTimes = isTap && typeof cc === "number" && Number.isInteger(cc) && cc >= 2 && cc <= 10 ? { times: cc } : {};
|
|
146021
145674
|
let step;
|
|
146022
145675
|
let warning;
|
|
146023
145676
|
if (captured?.selector) {
|
|
146024
|
-
step = { kind: "tap", selector: captured.selector
|
|
145677
|
+
step = { kind: "tap", selector: captured.selector };
|
|
146025
145678
|
warning = captured.warning;
|
|
146026
145679
|
} else if (isTap) {
|
|
146027
|
-
step = { kind: "tap", x: args.x, y: args.y
|
|
145680
|
+
step = { kind: "tap", x: args.x, y: args.y };
|
|
146028
145681
|
warning = captured?.warning;
|
|
146029
145682
|
} else if (isLaunch) {
|
|
146030
145683
|
step = { kind: "launch", app: strippedArgs.bundleId };
|
|
@@ -146114,20 +145767,11 @@ You can still edit the .yaml file directly afterwards to remove or reorder steps
|
|
|
146114
145767
|
return `${n}. run: ${step.flow}`;
|
|
146115
145768
|
case "tap":
|
|
146116
145769
|
return `${n}. tap: ${step.selector ? selectorLabel(step.selector) : `(${step.x}, ${step.y})`}`;
|
|
146117
|
-
case "long-press":
|
|
146118
|
-
return `${n}. long-press: ${selectorLabel(step.selector)}`;
|
|
146119
145770
|
case "type":
|
|
146120
145771
|
return `${n}. type: ${selectorLabel(step.into)} \u2190 "${step.text}"`;
|
|
146121
145772
|
case "await":
|
|
146122
145773
|
case "assert": {
|
|
146123
|
-
|
|
146124
|
-
if (step.condition !== "text") {
|
|
146125
|
-
tail = `${step.condition} ${selectorLabel(step.selector)}`;
|
|
146126
|
-
} else {
|
|
146127
|
-
const selector = selectorLabel(step.selector);
|
|
146128
|
-
const expected = step.expectedText ?? "";
|
|
146129
|
-
tail = step.textMatch === "matches" ? `text ${selector} matches /${expected}/` : step.textMatch === "equals" ? `text ${selector} == ${JSON.stringify(expected)}` : `text ${selector} contains ${JSON.stringify(expected)}`;
|
|
146130
|
-
}
|
|
145774
|
+
const tail = step.condition === "text" ? `text ${selectorLabel(step.selector)} == "${step.expectedText ?? ""}"` : `${step.condition} ${selectorLabel(step.selector)}`;
|
|
146131
145775
|
return `${n}. ${step.kind}: ${tail}`;
|
|
146132
145776
|
}
|
|
146133
145777
|
case "wait":
|
|
@@ -146369,7 +146013,7 @@ function offscreenHint(sel) {
|
|
|
146369
146013
|
return `no visible element matched selector ${describeSelector(sel)} \u2014 if it is off-screen, add a scroll-to step before this one`;
|
|
146370
146014
|
}
|
|
146371
146015
|
async function runDirective(env, step) {
|
|
146372
|
-
if (env.device.platform === "vega" && (step.kind === "tap" || step.kind === "
|
|
146016
|
+
if (env.device.platform === "vega" && (step.kind === "tap" || step.kind === "type" || step.kind === "scroll-to")) {
|
|
146373
146017
|
return {
|
|
146374
146018
|
ok: false,
|
|
146375
146019
|
reason: `${step.kind} is a touch directive and Vega is remote-driven \u2014 move focus with \`tool: tv-remote\` steps (and type via \`tool: keyboard\`) instead`
|
|
@@ -146378,8 +146022,6 @@ async function runDirective(env, step) {
|
|
|
146378
146022
|
switch (step.kind) {
|
|
146379
146023
|
case "tap":
|
|
146380
146024
|
return runTap(env, step);
|
|
146381
|
-
case "long-press":
|
|
146382
|
-
return runLongPress(env, step);
|
|
146383
146025
|
case "type":
|
|
146384
146026
|
return runType(env, step);
|
|
146385
146027
|
case "await":
|
|
@@ -146407,37 +146049,7 @@ async function runTap(env, target) {
|
|
|
146407
146049
|
} else {
|
|
146408
146050
|
return { ok: false, reason: "tap needs a selector or x/y coordinates" };
|
|
146409
146051
|
}
|
|
146410
|
-
await invokeOnDevice(env, "gesture-tap",
|
|
146411
|
-
...point,
|
|
146412
|
-
...target.times !== void 0 ? { clickCount: target.times } : {}
|
|
146413
|
-
});
|
|
146414
|
-
return { ok: true };
|
|
146415
|
-
}
|
|
146416
|
-
var DEFAULT_LONG_PRESS_MS = 800;
|
|
146417
|
-
async function runLongPress(env, step) {
|
|
146418
|
-
const frame = await waitForFrame(env, step.selector);
|
|
146419
|
-
if (frame === "aborted") return ABORTED_OUTCOME;
|
|
146420
|
-
if (!frame) {
|
|
146421
|
-
return { ok: false, reason: offscreenHint(step.selector) };
|
|
146422
|
-
}
|
|
146423
|
-
const point = getDescribeTapPoint(frame);
|
|
146424
|
-
const duration3 = step.duration ?? DEFAULT_LONG_PRESS_MS;
|
|
146425
|
-
if (env.device.platform === "chromium") {
|
|
146426
|
-
await invokeOnDevice(env, "gesture-drag", {
|
|
146427
|
-
fromX: point.x,
|
|
146428
|
-
fromY: point.y,
|
|
146429
|
-
toX: point.x,
|
|
146430
|
-
toY: point.y,
|
|
146431
|
-
durationMs: duration3
|
|
146432
|
-
});
|
|
146433
|
-
} else {
|
|
146434
|
-
await invokeOnDevice(env, "gesture-custom", {
|
|
146435
|
-
events: [
|
|
146436
|
-
{ type: "Down", x: point.x, y: point.y, delayMs: 0 },
|
|
146437
|
-
{ type: "Up", x: point.x, y: point.y, delayMs: duration3 }
|
|
146438
|
-
]
|
|
146439
|
-
});
|
|
146440
|
-
}
|
|
146052
|
+
await invokeOnDevice(env, "gesture-tap", point);
|
|
146441
146053
|
return { ok: true };
|
|
146442
146054
|
}
|
|
146443
146055
|
async function runType(env, step) {
|
|
@@ -146521,11 +146133,11 @@ function assertReason(condition, selector, expectedText, textMatch, matches2) {
|
|
|
146521
146133
|
case "text": {
|
|
146522
146134
|
const first = firstInReadingOrder(matches2.filter(isVisible)) ?? firstInReadingOrder(matches2);
|
|
146523
146135
|
if (!first) return `no element matched selector ${sel}`;
|
|
146524
|
-
const wanted =
|
|
146136
|
+
const wanted = textMatch === "equals" ? "equal" : "contain";
|
|
146525
146137
|
const shown = assertText(first);
|
|
146526
146138
|
const own = nodeText(first);
|
|
146527
146139
|
const ownNote = own && own !== shown ? ` (own text "${own}")` : "";
|
|
146528
|
-
return `element matched ${sel} but its text was "${shown}"${ownNote} (wanted to ${wanted})`;
|
|
146140
|
+
return `element matched ${sel} but its text was "${shown}"${ownNote} (wanted to ${wanted} "${expectedText}")`;
|
|
146529
146141
|
}
|
|
146530
146142
|
default:
|
|
146531
146143
|
return `assertion failed for selector ${sel}`;
|
|
@@ -148874,9 +148486,8 @@ function createRunFlowTool(registry2) {
|
|
|
148874
148486
|
id: "flow-execute",
|
|
148875
148487
|
description: `Run a saved flow from the .argent/flows/ directory.
|
|
148876
148488
|
Steps run in order: \`launch\` starts an app from scratch (terminate + relaunch) and waits until it is
|
|
148877
|
-
ready; \`tool\` calls dispatch through the registry; \`tap\`/\`
|
|
148878
|
-
|
|
148879
|
-
(momentum-free) until a target is visible; \`await\` waits for a UI
|
|
148489
|
+
ready; \`tool\` calls dispatch through the registry; \`tap\`/\`type\` resolve a selector to an element and
|
|
148490
|
+
act on it; \`scroll-to\` scrolls (momentum-free) until a target is visible; \`await\` waits for a UI
|
|
148880
148491
|
condition; \`wait\` pauses for a fixed number of milliseconds; \`assert\` checks one now; \`snapshot\`
|
|
148881
148492
|
diffs a screenshot against a stored baseline (a missing baseline fails the step \u2014 set updateBaselines
|
|
148882
148493
|
to adopt the current screen); \`echo\` annotates; \`run\` executes a referenced fragment inline.
|
|
@@ -149021,7 +148632,6 @@ function pushReport(state3, report) {
|
|
|
149021
148632
|
function selectorLabel2(sel) {
|
|
149022
148633
|
const parts = [];
|
|
149023
148634
|
if (sel.text !== void 0) parts.push(`"${sel.text}"`);
|
|
149024
|
-
if (sel.textMatches !== void 0) parts.push(`/${sel.textMatches}/`);
|
|
149025
148635
|
if (sel.identifier) parts.push(`id=${sel.identifier}`);
|
|
149026
148636
|
if (sel.role) parts.push(`role=${sel.role}`);
|
|
149027
148637
|
return parts.join(" ");
|
|
@@ -149032,15 +148642,13 @@ function stepTarget(step) {
|
|
|
149032
148642
|
if (step.selector) return selectorLabel2(step.selector);
|
|
149033
148643
|
if (step.x !== void 0 && step.y !== void 0) return `(${step.x}, ${step.y})`;
|
|
149034
148644
|
return void 0;
|
|
149035
|
-
case "long-press":
|
|
149036
|
-
return selectorLabel2(step.selector);
|
|
149037
148645
|
case "type":
|
|
149038
148646
|
return `into ${selectorLabel2(step.into)}`;
|
|
149039
148647
|
case "await":
|
|
149040
148648
|
case "assert": {
|
|
149041
148649
|
const sel = selectorLabel2(step.selector);
|
|
149042
148650
|
if (step.condition === "text") {
|
|
149043
|
-
return `${sel} ${
|
|
148651
|
+
return `${sel} ${step.textMatch ?? "contains"} "${step.expectedText ?? ""}"`;
|
|
149044
148652
|
}
|
|
149045
148653
|
return `${step.condition} ${sel}`;
|
|
149046
148654
|
}
|
|
@@ -149129,7 +148737,6 @@ async function execLeafStep(state3, step, index, sourceFlow) {
|
|
|
149129
148737
|
return { ...base, status: r.ok ? "pass" : "error", reason: r.reason };
|
|
149130
148738
|
}
|
|
149131
148739
|
case "tap":
|
|
149132
|
-
case "long-press":
|
|
149133
148740
|
case "type":
|
|
149134
148741
|
case "await":
|
|
149135
148742
|
case "assert":
|
|
@@ -149941,7 +149548,7 @@ function normLabel(s) {
|
|
|
149941
149548
|
return (s || "").toLowerCase().replace(/-/g, "").replace(/[\s,]+/g, " ").trim();
|
|
149942
149549
|
}
|
|
149943
149550
|
var MAX_FRAME_AREA = 0.85;
|
|
149944
|
-
function
|
|
149551
|
+
function matchNode2(n, match, needle) {
|
|
149945
149552
|
const label = normLabel(n.label);
|
|
149946
149553
|
const ident = normLabel(n.identifier);
|
|
149947
149554
|
const value = normLabel(n.value);
|
|
@@ -149968,7 +149575,7 @@ function findElementMatch(tree, match) {
|
|
|
149968
149575
|
const candidates = [];
|
|
149969
149576
|
const walk = (n) => {
|
|
149970
149577
|
if (!n || typeof n !== "object") return;
|
|
149971
|
-
const m =
|
|
149578
|
+
const m = matchNode2(n, match, needle);
|
|
149972
149579
|
if (m && n.frame) {
|
|
149973
149580
|
const f = n.frame;
|
|
149974
149581
|
const cx = f.x + f.width / 2;
|