@swmansion/argent 0.32.1-next.17 → 0.32.1-next.19
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/dist/cli-cmds.mjs +1 -1
- package/dist/installer.mjs +1 -1
- package/dist/mcp-server.mjs +76 -1
- package/dist/tool-args.generated.d.ts +6 -6
- package/dist/tool-server.cjs +42 -24
- package/package.json +1 -1
- package/skills/argent-create-e2e/references/reliability-and-recovery.md +1 -1
- package/skills/argent-device-interact/SKILL.md +1 -1
- package/skills/argent-device-interact/references/harmonyos.md +1 -1
- package/skills/argent-ios-simulator-setup/SKILL.md +1 -1
- package/skills/argent-screenshot-diff/SKILL.md +1 -1
- package/skills/argent-test-ui-flow/SKILL.md +1 -1
package/dist/cli-cmds.mjs
CHANGED
|
@@ -35871,7 +35871,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
35871
35871
|
var SESSION_ID2 = randomUUID5();
|
|
35872
35872
|
function readCliVersion() {
|
|
35873
35873
|
if (true) {
|
|
35874
|
-
return "0.32.1-next.
|
|
35874
|
+
return "0.32.1-next.18";
|
|
35875
35875
|
}
|
|
35876
35876
|
return "0.0.0";
|
|
35877
35877
|
}
|
package/dist/installer.mjs
CHANGED
|
@@ -16981,7 +16981,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
16981
16981
|
var SESSION_ID = randomUUID4();
|
|
16982
16982
|
function readCliVersion() {
|
|
16983
16983
|
if (true) {
|
|
16984
|
-
return "0.32.1-next.
|
|
16984
|
+
return "0.32.1-next.18";
|
|
16985
16985
|
}
|
|
16986
16986
|
return "0.0.0";
|
|
16987
16987
|
}
|
package/dist/mcp-server.mjs
CHANGED
|
@@ -29170,6 +29170,78 @@ function toMcpToolList(tools, options = {}) {
|
|
|
29170
29170
|
return tools.filter((t) => !t.hideFromMcp).map((t) => toMcpTool(t, options));
|
|
29171
29171
|
}
|
|
29172
29172
|
|
|
29173
|
+
// ../argent-mcp/src/list-devices-text.ts
|
|
29174
|
+
var RUNTIME_PREFIX = "com.apple.CoreSimulator.SimRuntime.";
|
|
29175
|
+
var STOPPED_SIMULATORS_HEADER = "iOS simulators that are shut down, one per line (platform, udid, name, runtime, state, tags); start one with boot-device:";
|
|
29176
|
+
function isRecord3(value) {
|
|
29177
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
29178
|
+
}
|
|
29179
|
+
function shortRuntime(runtime) {
|
|
29180
|
+
if (!runtime.startsWith(RUNTIME_PREFIX)) return runtime;
|
|
29181
|
+
const rest = runtime.slice(RUNTIME_PREFIX.length);
|
|
29182
|
+
const match = /^([A-Za-z]+)-(\d+(?:-\d+)*)$/.exec(rest);
|
|
29183
|
+
return match ? `${match[1]} ${match[2].replace(/-/g, ".")}` : rest;
|
|
29184
|
+
}
|
|
29185
|
+
function cleanEntry(device) {
|
|
29186
|
+
const out = {};
|
|
29187
|
+
for (const [key, value] of Object.entries(device)) {
|
|
29188
|
+
if (value === null || value === void 0) continue;
|
|
29189
|
+
if (key === "isEmulator") continue;
|
|
29190
|
+
if (key === "runtimeKind" && value === "mobile") continue;
|
|
29191
|
+
out[key] = key === "runtime" && typeof value === "string" ? shortRuntime(value) : value;
|
|
29192
|
+
}
|
|
29193
|
+
return out;
|
|
29194
|
+
}
|
|
29195
|
+
function isStoppedSimulator(device) {
|
|
29196
|
+
if (device.platform !== "ios" && device.platform !== "ios-remote") return false;
|
|
29197
|
+
if ("kind" in device || "external" in device) return false;
|
|
29198
|
+
return typeof device.udid === "string" && device.state === "Shutdown";
|
|
29199
|
+
}
|
|
29200
|
+
var ONE_LINE_FIELDS = /* @__PURE__ */ new Set(["platform", "udid", "name", "runtime", "state"]);
|
|
29201
|
+
function quoteIfNeeded(value) {
|
|
29202
|
+
return value === "" || /[\s"\\]/.test(value) ? JSON.stringify(value) : value;
|
|
29203
|
+
}
|
|
29204
|
+
function stoppedSimulatorLine(device) {
|
|
29205
|
+
const d = cleanEntry(device);
|
|
29206
|
+
const name = typeof d.name === "string" ? d.name : "";
|
|
29207
|
+
const parts = [String(d.platform), String(d.udid), JSON.stringify(name)];
|
|
29208
|
+
if (typeof d.runtime === "string") parts.push(d.runtime);
|
|
29209
|
+
if (typeof d.state === "string") parts.push(quoteIfNeeded(d.state));
|
|
29210
|
+
for (const [key, value] of Object.entries(d)) {
|
|
29211
|
+
if (ONE_LINE_FIELDS.has(key)) continue;
|
|
29212
|
+
if (value === true) parts.push(key);
|
|
29213
|
+
else if (typeof value === "string" || typeof value === "number")
|
|
29214
|
+
parts.push(`${key}=${quoteIfNeeded(String(value))}`);
|
|
29215
|
+
else parts.push(`${key}=${JSON.stringify(value)}`);
|
|
29216
|
+
}
|
|
29217
|
+
return parts.join(" ");
|
|
29218
|
+
}
|
|
29219
|
+
function listDevicesText(result) {
|
|
29220
|
+
if (!isRecord3(result) || !Array.isArray(result.devices)) return void 0;
|
|
29221
|
+
if (!result.devices.every(isRecord3)) return void 0;
|
|
29222
|
+
const devices = result.devices;
|
|
29223
|
+
const listed = [];
|
|
29224
|
+
const stopped = [];
|
|
29225
|
+
for (const device of devices) {
|
|
29226
|
+
if (isStoppedSimulator(device)) stopped.push(stoppedSimulatorLine(device));
|
|
29227
|
+
else listed.push(JSON.stringify(cleanEntry(device)));
|
|
29228
|
+
}
|
|
29229
|
+
const rest = [];
|
|
29230
|
+
for (const [key, value] of Object.entries(result)) {
|
|
29231
|
+
if (key === "devices") continue;
|
|
29232
|
+
rest.push(`${JSON.stringify(key)}:${JSON.stringify(value)}`);
|
|
29233
|
+
}
|
|
29234
|
+
const devicesPart = listed.length > 0 ? `[
|
|
29235
|
+
${listed.join(",\n")}
|
|
29236
|
+
]` : "[]";
|
|
29237
|
+
const object3 = `{"devices":${devicesPart}${rest.map((r) => `,
|
|
29238
|
+
${r}`).join("")}}`;
|
|
29239
|
+
if (stopped.length === 0) return object3;
|
|
29240
|
+
return `${object3}
|
|
29241
|
+
${STOPPED_SIMULATORS_HEADER}
|
|
29242
|
+
${stopped.join("\n")}`;
|
|
29243
|
+
}
|
|
29244
|
+
|
|
29173
29245
|
// ../argent-mcp/src/instructions.ts
|
|
29174
29246
|
var SERVER_INSTRUCTIONS = "Argent \u2014 iOS Simulator, Android Emulator, and Chromium app control for interacting, testing, profiling and debugging mobile and Chromium applications. Interaction tools return the screen after the action: a screenshot plus the accessibility element tree with normalized tap frames. Take coordinates from that tree; call describe only when no fresh tree is available \u2014 never guess coordinates from pixels. Do not stop simulator servers when a task finishes: the next task reuses them. Call stop-all-simulator-servers when the user asks to clean up or ends the session, or when a skill tells you to recycle a device's services, always with devices: [...] naming the devices this session used. Never stop a Metro the user started unless they ask. One tool-server is shared by every agent using this argent install, so an unscoped call tears down their devices too \u2014 reserve it for a deliberate machine-wide cleanup.";
|
|
29175
29247
|
|
|
@@ -29480,8 +29552,11 @@ ${hintLine}${desc}` }];
|
|
|
29480
29552
|
authToken: AUTH_TOKEN,
|
|
29481
29553
|
deviceId: getDeviceIdFromArgs(params.arguments)
|
|
29482
29554
|
};
|
|
29555
|
+
const devicesText = canonicalToolId(params.name) === "list-devices" ? listDevicesText(result) : void 0;
|
|
29483
29556
|
let content;
|
|
29484
|
-
if (
|
|
29557
|
+
if (devicesText !== void 0) {
|
|
29558
|
+
content = [{ type: "text", text: devicesText }];
|
|
29559
|
+
} else if (canonicalToolId(params.name) === "e2e-execute" && result && typeof result === "object" && ("e2eTest" in result || "flow" in result) && "steps" in result) {
|
|
29485
29560
|
content = await flowRunToMcpContent(result, ctx);
|
|
29486
29561
|
} else if (params.name === "screenshot-diff" && isScreenshotDiffResult(result)) {
|
|
29487
29562
|
content = await screenshotDiffToMcpContent(result, ctx);
|
|
@@ -506,7 +506,7 @@ export interface ArgentToolArgs {
|
|
|
506
506
|
includeAncestors?: boolean;
|
|
507
507
|
/** Include child views for each matched view (default true) */
|
|
508
508
|
includeChildren?: boolean;
|
|
509
|
-
/** View fields to include. Defaults: className,
|
|
509
|
+
/** View fields to include. Defaults: pointer, className, tag, frame, windowFrame, screenFrame, bounds, hidden, alpha, opaque, clipsToBounds, userInteractionEnabled, depth, identifier, label, layerName, nativeID. Additional: center, transform, contentMode, backgroundColor, tintColor, value, firstResponder */
|
|
510
510
|
fields?: Array<string>;
|
|
511
511
|
};
|
|
512
512
|
"native-full-hierarchy": {
|
|
@@ -514,7 +514,7 @@ export interface ArgentToolArgs {
|
|
|
514
514
|
udid: string;
|
|
515
515
|
/** Bundle ID of the app (on a Mac app, the bundle id in the device id) */
|
|
516
516
|
bundleId: string;
|
|
517
|
-
/** View fields to include. Use EXACT names:
|
|
517
|
+
/** View fields to include. Use EXACT names. Defaults: pointer, className, tag, frame, windowFrame, screenFrame, bounds, hidden, alpha, opaque, clipsToBounds, userInteractionEnabled, depth, identifier, label, layerName, nativeID. Additional: center, transform, contentMode, backgroundColor, tintColor, value, firstResponder */
|
|
518
518
|
fields?: Array<string>;
|
|
519
519
|
/** Exact UIView class names whose entire subtree should be pruned (e.g. ["UIImageView"] to drop image leaf nodes) */
|
|
520
520
|
skipClasses?: Array<string>;
|
|
@@ -560,7 +560,7 @@ export interface ArgentToolArgs {
|
|
|
560
560
|
udid: string;
|
|
561
561
|
/** Bundle ID of the app (on a Mac app, the bundle id in the device id) */
|
|
562
562
|
bundleId: string;
|
|
563
|
-
/** Raw X coordinate in points, on the portrait axes of the screen
|
|
563
|
+
/** Raw X coordinate in points, on the portrait axes of the screen; on a Mac app, points from the top-left of the area describe reports. NOT normalized [0,1] tap space. */
|
|
564
564
|
x: number;
|
|
565
565
|
/** Raw Y coordinate in points, on the same axes as x. NOT normalized [0,1] tap space. */
|
|
566
566
|
y: number;
|
|
@@ -568,7 +568,7 @@ export interface ArgentToolArgs {
|
|
|
568
568
|
includeAncestors?: boolean;
|
|
569
569
|
/** Include child views for the matched view (default false) */
|
|
570
570
|
includeChildren?: boolean;
|
|
571
|
-
/** View fields to include. Defaults: pointer, className, tag, frame, windowFrame, screenFrame, bounds, hidden, alpha, opaque, clipsToBounds, userInteractionEnabled, depth, identifier, label, layerName, nativeID. Additional: center, transform, contentMode, backgroundColor, tintColor */
|
|
571
|
+
/** View fields to include. Defaults: pointer, className, tag, frame, windowFrame, screenFrame, bounds, hidden, alpha, opaque, clipsToBounds, userInteractionEnabled, depth, identifier, label, layerName, nativeID. Additional: center, transform, contentMode, backgroundColor, tintColor, value, firstResponder */
|
|
572
572
|
fields?: Array<string>;
|
|
573
573
|
/** Exact UIView class names whose entire subtree should be pruned */
|
|
574
574
|
skipClasses?: Array<string>;
|
|
@@ -582,7 +582,7 @@ export interface ArgentToolArgs {
|
|
|
582
582
|
udid: string;
|
|
583
583
|
/** Bundle ID of the app (on a Mac app, the bundle id in the device id) */
|
|
584
584
|
bundleId: string;
|
|
585
|
-
/** Raw X coordinate in points, on the portrait axes of the screen
|
|
585
|
+
/** Raw X coordinate in points, on the portrait axes of the screen; on a Mac app, points from the top-left of the area describe reports. NOT normalized [0,1] tap space. */
|
|
586
586
|
x: number;
|
|
587
587
|
/** Raw Y coordinate in points, on the same axes as x. NOT normalized [0,1] tap space. */
|
|
588
588
|
y: number;
|
|
@@ -590,7 +590,7 @@ export interface ArgentToolArgs {
|
|
|
590
590
|
includeAncestors?: boolean;
|
|
591
591
|
/** Include child views for the matched view (default false) */
|
|
592
592
|
includeChildren?: boolean;
|
|
593
|
-
/** View fields to include. Defaults: pointer, className, tag, frame, windowFrame, screenFrame, bounds, hidden, alpha, opaque, clipsToBounds, userInteractionEnabled, depth, identifier, label, layerName, nativeID. Additional: center, transform, contentMode, backgroundColor, tintColor */
|
|
593
|
+
/** View fields to include. Defaults: pointer, className, tag, frame, windowFrame, screenFrame, bounds, hidden, alpha, opaque, clipsToBounds, userInteractionEnabled, depth, identifier, label, layerName, nativeID. Additional: center, transform, contentMode, backgroundColor, tintColor, value, firstResponder */
|
|
594
594
|
fields?: Array<string>;
|
|
595
595
|
/** Exact UIView class names whose entire subtree should be pruned */
|
|
596
596
|
skipClasses?: Array<string>;
|
package/dist/tool-server.cjs
CHANGED
|
@@ -101252,7 +101252,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
101252
101252
|
var SESSION_ID = (0, import_node_crypto3.randomUUID)();
|
|
101253
101253
|
function readCliVersion() {
|
|
101254
101254
|
if (true) {
|
|
101255
|
-
return "0.32.1-next.
|
|
101255
|
+
return "0.32.1-next.18";
|
|
101256
101256
|
}
|
|
101257
101257
|
return "0.0.0";
|
|
101258
101258
|
}
|
|
@@ -123698,7 +123698,7 @@ var NATIVE_DEVTOOLS_NAMESPACE = "NativeDevtools";
|
|
|
123698
123698
|
function isInjectableBundleId(bundleId) {
|
|
123699
123699
|
return !bundleId.toLowerCase().startsWith("com.apple.");
|
|
123700
123700
|
}
|
|
123701
|
-
var NON_INJECTABLE_NATIVE_WARNING = "Do not fall back to the native-devtools feature tools (native-
|
|
123701
|
+
var NON_INJECTABLE_NATIVE_WARNING = "Do not fall back to the native-devtools feature tools (native-find-views, native-full-hierarchy, native-network-logs, native-view-at-point, native-user-interactable-view-at-point): they run the same injection precheck and fail with the same non-injectable error.";
|
|
123702
123702
|
var NON_INJECTABLE_RECOVERY = "Use the standard `describe` tool (its accessibility path reads the screen without injection) or `screenshot` (then interact by coordinate). " + NON_INJECTABLE_NATIVE_WARNING;
|
|
123703
123703
|
var MAX_NATIVE_DEVTOOLS_INIT_ATTEMPTS = 3;
|
|
123704
123704
|
var NATIVE_DEVTOOLS_AGE_SLOP_MS = 3e3;
|
|
@@ -123865,7 +123865,7 @@ var nativeDevtoolsBlueprint = {
|
|
|
123865
123865
|
const { device } = opts;
|
|
123866
123866
|
if (device.platform !== "ios" && device.platform !== "ios-remote") {
|
|
123867
123867
|
throw new FailureError(
|
|
123868
|
-
`${NATIVE_DEVTOOLS_NAMESPACE} is iOS-only. The target '${device.id}' classifies as ${device.platform} \u2014 native-devtools tools (native-
|
|
123868
|
+
`${NATIVE_DEVTOOLS_NAMESPACE} is iOS-only. The target '${device.id}' classifies as ${device.platform} \u2014 native-devtools tools (native-find-views, native-view-at-point, etc.) only drive iOS simulators. Pick an iOS udid from list-devices.`,
|
|
123869
123869
|
{
|
|
123870
123870
|
error_code: FAILURE_CODES.NATIVE_DEVTOOLS_WRONG_PLATFORM,
|
|
123871
123871
|
failure_stage: "native_devtools_factory_options",
|
|
@@ -128234,6 +128234,8 @@ async function listRemoteIosSimulators() {
|
|
|
128234
128234
|
const result = await simctlListDevices();
|
|
128235
128235
|
const out = [];
|
|
128236
128236
|
for (const [runtime, devices] of Object.entries(result.devices)) {
|
|
128237
|
+
const runtimeKind = simulatorRuntimeKind(runtime);
|
|
128238
|
+
if (!runtimeKind) continue;
|
|
128237
128239
|
for (const d of devices) {
|
|
128238
128240
|
if (d.isAvailable === false) continue;
|
|
128239
128241
|
out.push({
|
|
@@ -128242,7 +128244,7 @@ async function listRemoteIosSimulators() {
|
|
|
128242
128244
|
name: d.name,
|
|
128243
128245
|
state: d.state,
|
|
128244
128246
|
runtime,
|
|
128245
|
-
runtimeKind
|
|
128247
|
+
runtimeKind
|
|
128246
128248
|
});
|
|
128247
128249
|
}
|
|
128248
128250
|
}
|
|
@@ -129112,6 +129114,7 @@ var describeNodeSchema = external_exports.lazy(
|
|
|
129112
129114
|
frame: describeFrameSchema,
|
|
129113
129115
|
children: external_exports.array(describeNodeSchema),
|
|
129114
129116
|
label: external_exports.string().optional(),
|
|
129117
|
+
hint: external_exports.string().optional(),
|
|
129115
129118
|
identifier: external_exports.string().optional(),
|
|
129116
129119
|
value: external_exports.string().optional(),
|
|
129117
129120
|
subtreeText: external_exports.string().optional(),
|
|
@@ -129191,8 +129194,11 @@ function adaptNativeDescribeElementToDescribeNode(element) {
|
|
|
129191
129194
|
frame: frame2,
|
|
129192
129195
|
children: [],
|
|
129193
129196
|
label: element.label,
|
|
129197
|
+
hint: element.hint,
|
|
129194
129198
|
identifier: element.identifier,
|
|
129195
|
-
value: element.value
|
|
129199
|
+
value: element.value,
|
|
129200
|
+
...element.traits.includes("notEnabled") ? { disabled: true } : {},
|
|
129201
|
+
...element.traits.includes("selected") ? { selected: true } : {}
|
|
129196
129202
|
};
|
|
129197
129203
|
}
|
|
129198
129204
|
function adaptNativeDescribeToDescribeResult(result) {
|
|
@@ -129236,10 +129242,13 @@ function adaptAXElement(el) {
|
|
|
129236
129242
|
},
|
|
129237
129243
|
children: [],
|
|
129238
129244
|
label: el.label,
|
|
129245
|
+
hint: el.hint,
|
|
129239
129246
|
value: el.value,
|
|
129240
129247
|
identifier: el.identifier,
|
|
129241
129248
|
// The text input that has the keyboard.
|
|
129242
|
-
...traits.includes("isEditing") ? { focused: true } : {}
|
|
129249
|
+
...traits.includes("isEditing") ? { focused: true } : {},
|
|
129250
|
+
...traits.includes("notEnabled") ? { disabled: true } : {},
|
|
129251
|
+
...traits.includes("selected") ? { selected: true } : {}
|
|
129243
129252
|
};
|
|
129244
129253
|
}
|
|
129245
129254
|
function adaptAXDescribeToDescribeResult(response) {
|
|
@@ -146136,7 +146145,7 @@ Returns { envSetup, appRunning, connected, requiresRestart, state, message, next
|
|
|
146136
146145
|
- nextLaunchWillBeInjected: if you launch this bundle now, native devtools env setup is already in place (always false for a non-injectable app)
|
|
146137
146146
|
- injectable: false for Apple system apps (bundle ids under com.apple.), which the native tools refuse to read.
|
|
146138
146147
|
|
|
146139
|
-
If injectable is false: treat this as TERMINAL. The app is not a supported native-devtools target, and no relaunch changes that. Do NOT restart/retry. Use the standard \`describe\` tool (its accessibility path reads the screen without injection) or \`screenshot\` (then interact by coordinate). Do not fall back to the native-devtools feature tools (native-
|
|
146148
|
+
If injectable is false: treat this as TERMINAL. The app is not a supported native-devtools target, and no relaunch changes that. Do NOT restart/retry. Use the standard \`describe\` tool (its accessibility path reads the screen without injection) or \`screenshot\` (then interact by coordinate). Do not fall back to the native-devtools feature tools (native-find-views, native-full-hierarchy, native-network-logs, native-view-at-point, native-user-interactable-view-at-point): they run the same injection precheck and fail with the same non-injectable error.
|
|
146140
146149
|
If appRunning is false and nextLaunchWillBeInjected is true: use launch-app normally.
|
|
146141
146150
|
If requiresRestart is true: call restart-app once, then proceed with the native feature. Read state before acting on a second such reading. Indeterminate reaches this rule too, and its line below bounds it at that one restart.
|
|
146142
146151
|
If state is unregistered: do NOT restart the app again: it already launched under the terms a restart would recreate. Restart the tool-server (\`argent server stop && argent server start --detach\`), then retry. If it reads unregistered again after that restart, stop: the process loads argent's dylib but never dials, and no further restart on either side changes it. Treat native devtools as unavailable, then use \`describe\` or \`screenshot\` and drive by coordinate.
|
|
@@ -146508,15 +146517,15 @@ var zodSchema5 = external_exports.object({
|
|
|
146508
146517
|
includeAncestors: external_exports.boolean().optional().describe("Include ancestor chain for each matched view (default true)"),
|
|
146509
146518
|
includeChildren: external_exports.boolean().optional().describe("Include child views for each matched view (default true)"),
|
|
146510
146519
|
fields: external_exports.array(external_exports.string()).optional().describe(
|
|
146511
|
-
"View fields to include. Defaults: className,
|
|
146520
|
+
"View fields to include. Defaults: pointer, className, tag, frame, windowFrame, screenFrame, bounds, hidden, alpha, opaque, clipsToBounds, userInteractionEnabled, depth, identifier, label, layerName, nativeID. Additional: center, transform, contentMode, backgroundColor, tintColor, value, firstResponder"
|
|
146512
146521
|
)
|
|
146513
146522
|
});
|
|
146514
146523
|
var nativeFindViewsTool = {
|
|
146515
146524
|
id: "native-find-views",
|
|
146516
146525
|
description: `Search for specific UIViews in the running app by class name, accessibility identifier, accessibility label, tag or React Native nativeID; at least one of these params is required.
|
|
146517
146526
|
Use when you need to locate a specific view by its properties without dumping the entire native-full-hierarchy.
|
|
146518
|
-
Returns { status: "ok", matches }: the matching views with their frames, properties, optional ancestors and optional children.
|
|
146519
|
-
<flag:macos-apps>On a Mac app it searches the NSViews of every visible window: className is the NSView class, and identifier matches the accessibility identifier or the view identifier. The statuses below are iOS only.
|
|
146527
|
+
Returns { status: "ok", matches }: the matching views with their frames, properties, optional ancestors and optional children. Frames are in points. frame and center are relative to the superview, and windowFrame turns with the UI. To tap a view, divide the centre of its screenFrame by the screen size in points, which native-full-hierarchy returns as screen (maxDepth: 1 is enough), and pass the result to gesture-tap. Matches are not filtered for visibility. Before tapping, call native-user-interactable-view-at-point at that centre with skipClasses ["UITextEffectsWindow"]. If it does not return the match or a descendant, check a screenshot.
|
|
146528
|
+
<flag:macos-apps>On a Mac app it searches the NSViews of every visible window: className is the NSView class, and identifier matches the accessibility identifier or the view identifier. The tap recipe above and the statuses below are iOS only.
|
|
146520
146529
|
</flag:macos-apps>If status is restart_required: follow the message (usually restart-app), then retry. If status is service_stale: the app is already injected, so restarting it cannot help. Restart the tool-server (\`argent server stop && argent server start --detach\`) and retry. If the same status comes back after that restart, stop restarting: follow the message, which names the terminal fallback. If status is connect_pending: the app is injected and still connecting. Do not restart it, wait a few seconds and retry. If status is init_failed: follow the message (re-boot the simulator) rather than retrying.
|
|
146521
146530
|
An Apple system app is rejected outright (terminal: never retry it).`,
|
|
146522
146531
|
zodSchema: zodSchema5,
|
|
@@ -146553,7 +146562,7 @@ init_zod();
|
|
|
146553
146562
|
var zodSchema6 = external_exports.object({
|
|
146554
146563
|
...nativeAppTarget,
|
|
146555
146564
|
fields: external_exports.array(external_exports.string()).optional().describe(
|
|
146556
|
-
"View fields to include. Use EXACT names:
|
|
146565
|
+
"View fields to include. Use EXACT names. Defaults: pointer, className, tag, frame, windowFrame, screenFrame, bounds, hidden, alpha, opaque, clipsToBounds, userInteractionEnabled, depth, identifier, label, layerName, nativeID. Additional: center, transform, contentMode, backgroundColor, tintColor, value, firstResponder"
|
|
146557
146566
|
),
|
|
146558
146567
|
skipClasses: external_exports.array(external_exports.string()).optional().describe(
|
|
146559
146568
|
'Exact UIView class names whose entire subtree should be pruned (e.g. ["UIImageView"] to drop image leaf nodes)'
|
|
@@ -146636,6 +146645,10 @@ normalized contract.
|
|
|
146636
146645
|
An Apple system app is rejected outright (terminal: never retry it).`,
|
|
146637
146646
|
zodSchema: zodSchema7,
|
|
146638
146647
|
readOnly: true,
|
|
146648
|
+
// Agents get the disabled and selected states and the VoiceOver hint from
|
|
146649
|
+
// `describe`, which falls back to this RPC (ViewHierarchy.describeScreen) when
|
|
146650
|
+
// the ax-service returns nothing. The SDK still calls this tool directly.
|
|
146651
|
+
hideFromMcp: true,
|
|
146639
146652
|
interaction: {
|
|
146640
146653
|
startedMsg: ({ params }) => `Reading native screen for ${params.bundleId}`,
|
|
146641
146654
|
completedMsg: ({ params }) => `Read native screen for ${params.bundleId}`,
|
|
@@ -146663,13 +146676,13 @@ init_src2();
|
|
|
146663
146676
|
var zodSchema8 = external_exports.object({
|
|
146664
146677
|
...nativeAppTarget,
|
|
146665
146678
|
x: external_exports.number().describe(
|
|
146666
|
-
"Raw X coordinate in points, on the portrait axes of the screen
|
|
146679
|
+
"Raw X coordinate in points, on the portrait axes of the screen<flag:macos-apps>; on a Mac app, points from the top-left of the area describe reports</flag:macos-apps>. NOT normalized [0,1] tap space."
|
|
146667
146680
|
),
|
|
146668
146681
|
y: external_exports.number().describe("Raw Y coordinate in points, on the same axes as x. NOT normalized [0,1] tap space."),
|
|
146669
146682
|
includeAncestors: external_exports.boolean().optional().describe("Include ancestor chain for the matched view (default true)"),
|
|
146670
146683
|
includeChildren: external_exports.boolean().optional().describe("Include child views for the matched view (default false)"),
|
|
146671
146684
|
fields: external_exports.array(external_exports.string()).optional().describe(
|
|
146672
|
-
"View fields to include. Defaults: pointer, className, tag, frame, windowFrame, screenFrame, bounds, hidden, alpha, opaque, clipsToBounds, userInteractionEnabled, depth, identifier, label, layerName, nativeID. Additional: center, transform, contentMode, backgroundColor, tintColor"
|
|
146685
|
+
"View fields to include. Defaults: pointer, className, tag, frame, windowFrame, screenFrame, bounds, hidden, alpha, opaque, clipsToBounds, userInteractionEnabled, depth, identifier, label, layerName, nativeID. Additional: center, transform, contentMode, backgroundColor, tintColor, value, firstResponder"
|
|
146673
146686
|
),
|
|
146674
146687
|
skipClasses: external_exports.array(external_exports.string()).optional().describe("Exact UIView class names whose entire subtree should be pruned"),
|
|
146675
146688
|
skipClassPrefixes: external_exports.array(external_exports.string()).optional().describe("Class name prefixes to prune entire subtrees"),
|
|
@@ -146691,7 +146704,8 @@ identifier, label and layer name, plus its ancestors and subviews as requested.
|
|
|
146691
146704
|
view is null when nothing is drawn at that point.
|
|
146692
146705
|
|
|
146693
146706
|
To get x and y from a normalized describe frame, multiply its values by the
|
|
146694
|
-
screen size in points, also when the UI is landscape
|
|
146707
|
+
screen size in points, also when the UI is landscape.
|
|
146708
|
+
native-full-hierarchy returns that size as screen (pass maxDepth: 1 and fields: ["className"] to keep it small).<flag:macos-apps> On a Mac app,
|
|
146695
146709
|
multiply them by the size of the area describe reports instead.</flag:macos-apps>
|
|
146696
146710
|
|
|
146697
146711
|
If status is restart_required: follow the message (usually restart-app), then retry. If status is service_stale: the app is already injected, so restarting it cannot help. Restart the tool-server (\`argent server stop && argent server start --detach\`) and retry. If the same status comes back after that restart, stop restarting: follow the message, which names the terminal fallback. If status is connect_pending: the app is injected and still connecting. Do not restart it, wait a few seconds and retry. If status is init_failed: follow the message (re-boot the simulator) rather than retrying.
|
|
@@ -146725,13 +146739,13 @@ init_src2();
|
|
|
146725
146739
|
var zodSchema9 = external_exports.object({
|
|
146726
146740
|
...nativeAppTarget,
|
|
146727
146741
|
x: external_exports.number().describe(
|
|
146728
|
-
"Raw X coordinate in points, on the portrait axes of the screen
|
|
146742
|
+
"Raw X coordinate in points, on the portrait axes of the screen<flag:macos-apps>; on a Mac app, points from the top-left of the area describe reports</flag:macos-apps>. NOT normalized [0,1] tap space."
|
|
146729
146743
|
),
|
|
146730
146744
|
y: external_exports.number().describe("Raw Y coordinate in points, on the same axes as x. NOT normalized [0,1] tap space."),
|
|
146731
146745
|
includeAncestors: external_exports.boolean().optional().describe("Include ancestor chain for the matched view (default true)"),
|
|
146732
146746
|
includeChildren: external_exports.boolean().optional().describe("Include child views for the matched view (default false)"),
|
|
146733
146747
|
fields: external_exports.array(external_exports.string()).optional().describe(
|
|
146734
|
-
"View fields to include. Defaults: pointer, className, tag, frame, windowFrame, screenFrame, bounds, hidden, alpha, opaque, clipsToBounds, userInteractionEnabled, depth, identifier, label, layerName, nativeID. Additional: center, transform, contentMode, backgroundColor, tintColor"
|
|
146748
|
+
"View fields to include. Defaults: pointer, className, tag, frame, windowFrame, screenFrame, bounds, hidden, alpha, opaque, clipsToBounds, userInteractionEnabled, depth, identifier, label, layerName, nativeID. Additional: center, transform, contentMode, backgroundColor, tintColor, value, firstResponder"
|
|
146735
146749
|
),
|
|
146736
146750
|
skipClasses: external_exports.array(external_exports.string()).optional().describe("Exact UIView class names whose entire subtree should be pruned"),
|
|
146737
146751
|
skipClassPrefixes: external_exports.array(external_exports.string()).optional().describe("Class name prefixes to prune entire subtrees"),
|
|
@@ -146753,7 +146767,8 @@ identifier, label and layer name, plus its ancestors and subviews as requested.
|
|
|
146753
146767
|
view is null when no touchable view sits under that point.
|
|
146754
146768
|
|
|
146755
146769
|
To get x and y from a normalized describe frame, multiply its values by the
|
|
146756
|
-
screen size in points, also when the UI is landscape
|
|
146770
|
+
screen size in points, also when the UI is landscape.
|
|
146771
|
+
native-full-hierarchy returns that size as screen (pass maxDepth: 1 and fields: ["className"] to keep it small).<flag:macos-apps> On a Mac app,
|
|
146757
146772
|
multiply them by the size of the area describe reports instead.</flag:macos-apps>
|
|
146758
146773
|
|
|
146759
146774
|
If status is restart_required: follow the message (usually restart-app), then retry. If status is service_stale: the app is already injected, so restarting it cannot help. Restart the tool-server (\`argent server stop && argent server start --detach\`) and retry. If the same status comes back after that restart, stop restarting: follow the message, which names the terminal fallback. If status is connect_pending: the app is injected and still connecting. Do not restart it, wait a few seconds and retry. If status is init_failed: follow the message (re-boot the simulator) rather than retrying.
|
|
@@ -154084,8 +154099,7 @@ var gestureTapTool = {
|
|
|
154084
154099
|
description: `Press the device screen (iOS simulator or physical device, Android emulator, <flag:harmonyos>HarmonyOS device, </flag:harmonyos><flag:macos-apps>Mac app, </flag:macos-apps>or Chromium app) at a point given as fractions of the screen width and height (not pixels): a Down event followed by an Up event at the same spot.<flag:macos-apps> On a Mac app it is a click, and x/y are fractions of the area describe reports, not of the window.</flag:macos-apps>
|
|
154085
154100
|
Use when tapping a button, link, or any tappable element on the screen.
|
|
154086
154101
|
Returns { tapped: true, timestampMs }. On physical iOS, reactivated: true = app was re-fronted; re-describe. Fails if the device backend (simulator-server / emulator / Chromium CDP<flag:harmonyos> / \`hdc\`</flag:harmonyos>) is unreachable.
|
|
154087
|
-
|
|
154088
|
-
Before tapping, determine the correct coordinates by using discovery tools. Pick by platform: iOS / Android use \`describe\` (on an iOS simulator, also \`native-describe-screen\`); Chromium uses \`describe\` (the DOM walker)<flag:harmonyos> and HarmonyOS uses \`describe\` (the \`uitest\` layout dump)</flag:harmonyos>, since the native discovery tools don't apply<flag:harmonyos> to either</flag:harmonyos><flag:macos-apps>; a Mac app uses \`describe\`</flag:macos-apps>. More information in \`argent-device-interact\` skill`,
|
|
154102
|
+
Before tapping, take the coordinates from \`describe\` on every platform (on Chromium it walks the DOM<flag:harmonyos>, on HarmonyOS it reads the \`uitest\` layout dump</flag:harmonyos>). More information in \`argent-device-interact\` skill`,
|
|
154089
154103
|
zodSchema: zodSchema18,
|
|
154090
154104
|
alwaysLoad: true,
|
|
154091
154105
|
// A physical iPhone's first call waits out the runner build, past the MCP fetch timeout,
|
|
@@ -160089,19 +160103,24 @@ function hasContent(n) {
|
|
|
160089
160103
|
function shouldEmit(n, contentRoles) {
|
|
160090
160104
|
return hasContent(n) || contentRoles.has(n.role);
|
|
160091
160105
|
}
|
|
160106
|
+
function hasIosState(n) {
|
|
160107
|
+
return Boolean(n.hint || n.disabled || n.selected);
|
|
160108
|
+
}
|
|
160092
160109
|
function formatLine(n, indent, source) {
|
|
160093
160110
|
const pad = " ".repeat(indent);
|
|
160094
160111
|
const dedupedValue = n.value && n.value !== n.label ? n.value : void 0;
|
|
160112
|
+
const dedupedHint = n.hint && n.hint !== n.label ? n.hint : void 0;
|
|
160095
160113
|
const labelPart = formatLabel(n.label);
|
|
160096
160114
|
const valuePart = formatAttr("value", dedupedValue);
|
|
160097
160115
|
const idPart = formatAttr("id", n.identifier);
|
|
160116
|
+
const hintPart = formatAttr("hint", dedupedHint);
|
|
160098
160117
|
const flagPart = formatFlags(n, source);
|
|
160099
|
-
const annotations = `${labelPart}${valuePart}${idPart}${flagPart}`.trim();
|
|
160118
|
+
const annotations = `${labelPart}${valuePart}${idPart}${hintPart}${flagPart}`.trim();
|
|
160100
160119
|
const annotated = annotations ? ` ${annotations}` : "";
|
|
160101
160120
|
return `${pad}${n.role}${annotated} ${fmtFrame(n.frame)}`;
|
|
160102
160121
|
}
|
|
160103
160122
|
function renderFlat(root, contentRoles, source) {
|
|
160104
|
-
return root.children.filter((n) => shouldEmit(n, contentRoles)).slice().sort((a, b) => a.frame.y - b.frame.y || a.frame.x - b.frame.x).map((n) => formatLine(n, 1, source));
|
|
160123
|
+
return root.children.filter((n) => shouldEmit(n, contentRoles) || hasIosState(n)).slice().sort((a, b) => a.frame.y - b.frame.y || a.frame.x - b.frame.x).map((n) => formatLine(n, 1, source));
|
|
160105
160124
|
}
|
|
160106
160125
|
function renderNested(root, contentRoles, source) {
|
|
160107
160126
|
const lines = [];
|
|
@@ -160317,11 +160336,10 @@ On HarmonyOS it runs the device's \`uitest dumpLayout\` (\`source: "harmony-uite
|
|
|
160317
160336
|
On a Mac app it reads the app's accessibility tree with web view content and React Native testIDs merged in; frames are fractions of the app window, grown to include sheets and menus past it.</flag:macos-apps>
|
|
160318
160337
|
On a TV target (Apple TV / Android TV: a \`list-devices\` entry with runtimeKind 'tv') it returns the FOCUSED element and the FOCUSABLE elements instead, since a TV UI has no tap coordinates. Move the highlight with \`tv-remote\`, then call describe again to confirm where focus landed.
|
|
160319
160338
|
|
|
160320
|
-
Returns \`{ description, source }\`: \`description\` renders the tree one line per element, with its role, label/value/id, interactivity flags and frame. Frames are normalized [0,1] fractions of the screen / window width and height (not pixels); tap an element at the centre of its frame. The tree has no z-order or occlusion information: an element may be covered by an overlay (e.g. a toolbar over list rows), so check a screenshot when a tap lands unexpectedly.
|
|
160339
|
+
Returns \`{ description, source }\`: \`description\` renders the tree one line per element, with its role, label/value/id, VoiceOver hint (iOS simulator), interactivity flags and frame. Frames are normalized [0,1] fractions of the screen / window width and height (not pixels); tap an element at the centre of its frame. The tree has no z-order or occlusion information: an element may be covered by an overlay (e.g. a toolbar over list rows), so check a screenshot when a tap lands unexpectedly.
|
|
160321
160340
|
Fails if the tree cannot be read, e.g. on a physical iOS device before launch-app has started an app.
|
|
160322
160341
|
|
|
160323
|
-
For
|
|
160324
|
-
use native-describe-screen with an explicit bundleId instead (iOS simulator only).
|
|
160342
|
+
For UIKit properties of a view (class name, accessibilityIdentifier), use native-find-views (iOS simulator only).
|
|
160325
160343
|
For React Native component names, or a testID describe does not list, use debugger-component-tree.`,
|
|
160326
160344
|
zodSchema: zodSchema40,
|
|
160327
160345
|
alwaysLoad: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swmansion/argent",
|
|
3
|
-
"version": "0.32.1-next.
|
|
3
|
+
"version": "0.32.1-next.19",
|
|
4
4
|
"mcpName": "io.github.software-mansion/argent",
|
|
5
5
|
"description": "MCP server for iOS Simulator, Android Emulator, Chromium app and Mac app control",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -26,7 +26,7 @@ Convert element-seeking swipes to `scroll-to`. Keep a coordinate swipe only when
|
|
|
26
26
|
|
|
27
27
|
Work this gate as soon as capture warns that it kept a raw point or recorded a role-only selector. Keep the source screen available and do these checks:
|
|
28
28
|
|
|
29
|
-
1. **iOS:** query plausible ids or labels with `native-find-views`. If no term is useful, call `native-full-hierarchy` with narrow fields and `maxDepth: 100`. `describe`
|
|
29
|
+
1. **iOS:** query plausible ids or labels with `native-find-views`. If no term is useful, call `native-full-hierarchy` with narrow fields and `maxDepth: 100`. `describe` is an accessibility projection. It cannot prove that no YAML selector exists.
|
|
30
30
|
2. **Other platforms:** use `describe`; for React Native on Android, also `debugger-component-tree`, for testIDs `describe` omits. Verify Android and Chromium candidates in step 3. Their discovery trees can omit runner elements.
|
|
31
31
|
3. Test each candidate in a scratch fragment with `assert: { visible: <candidate> }` on the valid screen. Inspect every failure before trying a better id, label, app, or container.
|
|
32
32
|
4. If source is available, inspect its `testID`, `accessibilityIdentifier`, or `resource-id`. If none exists, report the missing stable id as the real fix.
|
|
@@ -45,7 +45,7 @@ After an action moves you to a different screen, or whenever you don't know an e
|
|
|
45
45
|
| Need | Tool |
|
|
46
46
|
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
|
47
47
|
| Any screen: app, system dialog, Home screen | `describe` |
|
|
48
|
-
|
|
|
48
|
+
| UIKit properties of a view (iOS simulator) | `native-find-views` (needs `bundleId` and a class, id, label, tag or `nativeID` to match) |
|
|
49
49
|
| What sits at a known raw point (iOS simulator) | `native-user-interactable-view-at-point` (the view that receives touch) or `native-view-at-point` (the visually deepest view) |
|
|
50
50
|
| Final visual fallback | `screenshot`, only when the tools above cannot expose the control. Do not derive routine in-app navigation targets from pixels |
|
|
51
51
|
|
|
@@ -17,7 +17,7 @@ Refused as unsupported on harmony:
|
|
|
17
17
|
|
|
18
18
|
- `gesture-rotate` and `gesture-custom`: the device moves each contact along one straight line per call, so there is no arc and no long-press. There is no long-press tool on HarmonyOS.
|
|
19
19
|
- `gesture-drag` and `gesture-scroll` (Chromium-only), and `rotate`: the device has no orientation command.
|
|
20
|
-
- `native-
|
|
20
|
+
- `native-view-at-point`, `native-user-interactable-view-at-point`, `debugger-component-tree`, the rest of the debugger (`debugger-evaluate`, `debugger-log-registry`), `view-network-logs` and the profilers.
|
|
21
21
|
- `paste`, so a `{{secret:...}}` placeholder goes through `keyboard` only.
|
|
22
22
|
- Screen recording: HarmonyOS exposes no readable frame stream. Use `screenshot` for a still frame.
|
|
23
23
|
|
|
@@ -5,7 +5,7 @@ description: Set up and connect to an iOS simulator using argent MCP tools. Use
|
|
|
5
5
|
|
|
6
6
|
## 1. Setup Steps
|
|
7
7
|
|
|
8
|
-
1. **Find a booted simulator.** Use `list-devices`. Filter for entries with `platform: "ios"` and skip `kind: "device"` (a physical iPhone): booted simulators are listed first. If no fitting simulator is booted, call `boot-device` with `udid: <chosen UDID>`.
|
|
8
|
+
1. **Find a booted simulator.** Use `list-devices`. Filter for entries with `platform: "ios"` and skip `kind: "device"` (a physical iPhone): booted simulators are listed first. Through MCP, simulators that are shut down follow the JSON object, one line each, with the udid second. If no fitting simulator is booted, call `boot-device` with `udid: <chosen UDID>`.
|
|
9
9
|
2. **No connect step.** Interaction tools (`gesture-tap`, `describe`, `screenshot`, …) auto-start the simulator server on first use.
|
|
10
10
|
|
|
11
11
|
## 2. Notes
|
|
@@ -7,7 +7,7 @@ description: Compare saved or live app screenshots with the argent screenshot-di
|
|
|
7
7
|
|
|
8
8
|
Use `screenshot-diff` as supporting visual evidence for UI QA and visual regression checks. It highlights pixel-visible change or stability; it does not replace visual inspection, accessibility/component-tree state, frame/attribute checks, logs, network evidence, or app behavior. Its description covers the input combinations, physical-iPhone captures and the result.
|
|
9
9
|
|
|
10
|
-
Do not use screenshot diffing for tap-coordinate discovery. Use `describe`
|
|
10
|
+
Do not use screenshot diffing for tap-coordinate discovery. Use `describe` to find targets first.
|
|
11
11
|
|
|
12
12
|
## 2. When To Use
|
|
13
13
|
|
|
@@ -20,7 +20,7 @@ For implementation tasks that modify visible UI, this workflow also serves as a
|
|
|
20
20
|
3. **Interact**: perform the action (`gesture-tap`, `gesture-swipe`, `keyboard`, `button`, ...); you receive a screenshot automatically.
|
|
21
21
|
4. **Verify**: check the returned screenshot. If it shows a loading/transitional state, block with `await-ui-element` (expected element `visible`, or a spinner `hidden`) rather than a guessed delay (its result carries the new screenshot and element tree), but only with a selector you can trust (`text`/`identifier`/`role`) that the screen is known to have or that you saw in a prior `describe`; a guessed one just times out. Otherwise use a short fixed wait. Pick evidence by what's being asserted:
|
|
22
22
|
- **Visual** (layout, spacing, color, typography, image/icon rendering, clipping, overflow, text rendering): prefer `screenshot-diff` against the step 1 baseline: it surfaces pixel-visible changes the auto-screenshot might miss. Fall back to inspecting the auto-screenshot only when a stable baseline isn't available.
|
|
23
|
-
- **Structural** (navigation state, element existence, accessibility labels/values, selection, hierarchy, route): verify with `describe
|
|
23
|
+
- **Structural** (navigation state, element existence, accessibility labels/values, selection, hierarchy, route): verify with `describe` or `debugger-component-tree`.
|
|
24
24
|
- **Runtime / log / network** (console errors, API calls, persistence, timing): verify with `view-network-logs`, `debugger-log-registry`, `debugger-evaluate`, or targeted tests. A `not_connected` result from the debugger tools is not evidence about the app: follow its `guidance`, then re-verify.
|
|
25
25
|
- **Mixed**: collect evidence for each relevant class.
|
|
26
26
|
- Report the combined verdict: expected behavior, observed behavior, evidence used, and any blocker for requested visual diffing.
|