diff --git a/README.md b/README.md index 320d39707..2327d02fd 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,67 @@ Argent drives a growing set of targets through a single toolkit, each with the r --- +## Physical iOS devices (experimental) + +Argent can drive a connected **physical iPhone** through the same tool interfaces used for an +iOS simulator. It automatically builds, signs, installs, and reuses +[WebDriverAgent](https://github.com/appium/WebDriverAgent) for XCTest input and accessibility; +the target app itself does not need to be modified or instrumented. The phone appears in +`list-devices` with `kind: "device"`. + +The physical backend supports: + +- tap, swipe, drag/pan, long press/hold, arbitrary touch sequences, pinch, and rotate gestures; +- keyboard text and named keys, paste, Home/Power/volume/Action buttons, and orientation; +- `screenshot`, nested live `describe`, `screenshot-diff`, app launch/restart, deep links, and + installing a device-signed `.app`; +- continuous Apple unified logs with `device-logs-start` / `device-logs-stop`; +- `native-profiler-*` for any foreground app, including protected system apps such as Maps. + Physical profiling records device-wide Time Profiler data and filters CPU/hang analysis to the + app PID, avoiding the task-port restriction that prevents a direct Instruments attach. + +**Requirements** + +- macOS with Xcode and an Apple Development team configured in Xcode Settings → Accounts. +- iOS 17 or later for the complete keyboard/control surface (the backend is tested on iOS 26.2). +- The iPhone connected, unlocked, trusted, with **Developer Mode** on. +- [`pymobiledevice3`](https://github.com/doronz88/pymobiledevice3) for physical-device unified + log streaming (for example, `pipx install pymobiledevice3`). + +**Setup** + +1. Enable the feature flag: + ```sh + argent enable physical-ios-devices + ``` +2. Connect the iPhone (unlocked, trusted, Developer Mode on). + +`list-devices` then includes the iPhone, and the supported tools work against its UDID. +The first `boot-device`, `launch-app`, or control call prepares WebDriverAgent. Argent discovers +the Xcode development team and signing identity, caches the signed runner under +`~/.argent/webdriveragent`, and keeps one WDA session per phone. No root tunnel or admin prompt is +needed for the XCTest path. A cold first preparation takes several seconds; later controls reuse +the session. + +**Limitations / notes** + +- Physical controls are registered on a strict local queue and acknowledged on the simulator's + gesture cadence. Read operations, log stop, and profiler stop are barriers, so screenshots and + artifacts always include all earlier controls even though XCTest may finish them later. +- `describe` is the frontmost app's exact nested XCTest hierarchy, including labels, values, + element roles, and frames in the same normalized coordinate space as the gesture tools. +- Device-wide profiling provides CPU and potential-hang analysis for protected apps. Apple's + Leaks/Allocations instruments require a process-scoped task port, so they are not available for + protected system apps. +- Simulator-only injected `native-*` UIKit/React inspection remains unavailable on a physical + target. Use `describe`, screenshots, unified logs, and native profiling instead. +- XCTest does not expose the App Switcher gesture. Power locks the device; unlocking may still + require the device passcode. +- Signing/logging overrides: `ARGENT_WDA_TEAM_ID`, `ARGENT_WDA_BUNDLE_ID`, + `ARGENT_WDA_SIGNING_ID`, and `ARGENT_PYMOBILEDEVICE3`. + +--- + ## Installation #### Prerequisites diff --git a/package-lock.json b/package-lock.json index ffa6ad263..0f528b3c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,423 @@ "typescript-eslint": "^8.62.0" } }, + "node_modules/@appium/base-driver": { + "version": "10.7.1", + "resolved": "https://registry.npmjs.org/@appium/base-driver/-/base-driver-10.7.1.tgz", + "integrity": "sha512-DE6TcCBvoL5N+5zjbwhyVvnfzTvvu1ZFLULjK8MZuPQPklLc8nRpYIgKIqvsGDUp2QH29Gs0SQDqO9XebOX1Jw==", + "license": "Apache-2.0", + "dependencies": { + "@appium/support": "7.2.5", + "@appium/types": "1.5.1", + "async-lock": "1.4.1", + "asyncbox": "6.3.0", + "axios": "1.18.0", + "body-parser": "2.2.2", + "express": "5.2.1", + "fastest-levenshtein": "1.0.16", + "http-status-codes": "2.3.0", + "lru-cache": "11.5.1", + "method-override": "3.0.0", + "morgan": "1.11.0", + "path-to-regexp": "8.4.2", + "serve-favicon": "2.5.1", + "type-fest": "5.7.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + }, + "optionalDependencies": { + "spdy": "4.0.2" + } + }, + "node_modules/@appium/base-driver/node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@appium/base-driver/node_modules/type-fest": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.7.0.tgz", + "integrity": "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==", + "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@appium/base-plugin": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@appium/base-plugin/-/base-plugin-3.3.2.tgz", + "integrity": "sha512-b3vs/qrR9nJG4HLipB+FT2Y3H0xXNZp4nVDZfVsnk7ZeMJEJNb8Uf11lEEnl2zeAZk8+dsnbch824VKBHuq18w==", + "license": "Apache-2.0", + "dependencies": { + "@appium/base-driver": "10.7.1", + "@appium/support": "7.2.5", + "@appium/types": "1.5.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + } + }, + "node_modules/@appium/docutils": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@appium/docutils/-/docutils-2.5.1.tgz", + "integrity": "sha512-P2Kveb4I68KGC6gAY0TRTVgvqBaZW3WqpnHgK0iiN+lLhaxBoRrqoaGJR95oPb93sgkZQT59ZexdZ5KXf8wFtA==", + "license": "Apache-2.0", + "dependencies": { + "@appium/support": "7.2.5", + "consola": "3.4.2", + "diff": "9.0.0", + "lilconfig": "3.1.3", + "normalize-package-data": "8.0.0", + "teen_process": "4.1.3", + "type-fest": "5.7.0", + "yaml": "2.9.0", + "yargs": "18.0.0", + "yargs-parser": "22.0.0" + }, + "bin": { + "appium-docs": "bin/appium-docs.js" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + } + }, + "node_modules/@appium/docutils/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@appium/docutils/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@appium/docutils/node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@appium/docutils/node_modules/diff": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", + "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/@appium/docutils/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, + "node_modules/@appium/docutils/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@appium/docutils/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@appium/docutils/node_modules/type-fest": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.7.0.tgz", + "integrity": "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==", + "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@appium/docutils/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@appium/docutils/node_modules/yargs": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", + "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^7.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/@appium/docutils/node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/@appium/logger": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@appium/logger/-/logger-2.0.9.tgz", + "integrity": "sha512-3rNqtyxTygrDkxEMA2UB35EQfzxlnGCkIJLdIRDLKhVfbHgFw8XEnPnYuvMKvzG8zVeBUoh76dxKVHyIezPsIQ==", + "license": "ISC", + "dependencies": { + "lru-cache": "11.5.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + } + }, + "node_modules/@appium/schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@appium/schema/-/schema-1.2.1.tgz", + "integrity": "sha512-+XmwihFeW10Q0UI/WdLsMTfnC2d2ec0ceGRdhRcfBuo5cClfm/E7VCJH2PE64KE0A17ZlsZf63FRkKfNHw9PKw==", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "0.4.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + } + }, + "node_modules/@appium/support": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/@appium/support/-/support-7.2.5.tgz", + "integrity": "sha512-+sk7HdWYD5EMcSVtiMK09aA960suBJY/8d+r5v8j9vHw47R4ukLrM2lShpIpFFCTTfz5ZTCjCtvqNwwM8EqyMg==", + "license": "Apache-2.0", + "dependencies": { + "@appium/logger": "2.0.9", + "@appium/types": "1.5.1", + "archiver": "8.0.0", + "asyncbox": "6.3.0", + "axios": "1.18.0", + "bluebird": "3.7.2", + "bplist-creator": "0.1.1", + "bplist-parser": "0.3.2", + "form-data": "4.0.6", + "glob": "13.0.6", + "jsftp": "2.1.3", + "klaw": "4.1.0", + "lockfile": "1.0.4", + "normalize-package-data": "8.0.0", + "plist": "4.0.0", + "pluralize": "8.0.0", + "sanitize-filename": "1.6.4", + "semver": "7.8.4", + "shell-quote": "1.8.4", + "teen_process": "4.1.3", + "type-fest": "5.7.0", + "uuid": "14.0.0", + "which": "6.0.1", + "yauzl": "3.4.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + }, + "optionalDependencies": { + "sharp": "0.35.1" + } + }, + "node_modules/@appium/support/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@appium/support/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/@appium/support/node_modules/semver": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@appium/support/node_modules/type-fest": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.7.0.tgz", + "integrity": "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==", + "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@appium/support/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@appium/types": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@appium/types/-/types-1.5.1.tgz", + "integrity": "sha512-cQkbSh6AwEGlowfKBxVngtBc5fkqAPmg4WdjGZ2txa81lpfuNTLX50VWM8Nxf3hiCgRMsRVfBWdU9x38OJqDrQ==", + "license": "Apache-2.0", + "dependencies": { + "@appium/schema": "1.2.1", + "type-fest": "5.7.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + }, + "peerDependencies": { + "@appium/logger": "^2.0.0" + } + }, + "node_modules/@appium/types/node_modules/type-fest": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.7.0.tgz", + "integrity": "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==", + "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@argent/archive": { "resolved": "packages/archive", "link": true @@ -105,6 +522,15 @@ "node": ">= 20.12.0" } }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "dev": true, @@ -116,6 +542,17 @@ "node": ">=12" } }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", + "license": "MIT", + "dependencies": { + "@so-ric/colorspace": "^1.1.6", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "node_modules/@electron-internal/extract-zip": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@electron-internal/extract-zip/-/extract-zip-1.0.3.tgz", @@ -147,6 +584,39 @@ "undici": "^7.24.4" } }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", + "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.28.1", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", @@ -833,1282 +1303,5585 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.1.tgz", + "integrity": "sha512-T15JRWOubQ3f5+GxnWeIvo47u5qV0M9HBgJhT+f2gE1e9e6OhR6K73Re52Hm80qWcu1DNb3GweKmpr/MnuP2Ow==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.3.0" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.1.tgz", + "integrity": "sha512-t1CPD0cr7XCHjwUj6tQ5MC0pCi866I+gUW6zbUX4aFPnKd1DFBtk0M+gWcjX8VeEzgfCNiSiNTVFZ6b7kvdbnQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.3.0" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.1.tgz", + "integrity": "sha512-MBSQXqNPThW9EcZ905H6N4sEdX5EwZEYzGx5EBq9ncDCGJALMiY1xPFJxNdzuB1iBjLOpIfxajM6YxdvwmQSLA==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.0.tgz", + "integrity": "sha512-EKbmBKtyTH+GPFDRw2TgK2oV6hyxxlJVIar4hoTYSNmIwipgMFdxPQqR392GmfdsPGWga0mCFN1cCKjRb9cljw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.0.tgz", + "integrity": "sha512-Pl2OmOvrJ42adUllESxBsG54PfXLo1OYg9i3c5/5Ln/qJ0gZuTM9YMhQJPIbXqwidLRc/c2zuHt4RsrymmNv7A==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.0.tgz", + "integrity": "sha512-A8UpHoUDW4DwnXoV6+q3C1s7QLRAHtPDEjWuNZjwHMyoCNZnm0GeNN8ls9f/bsEYTRQRW96C/n34XJQHJ2fT7A==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.0.tgz", + "integrity": "sha512-C0SqjoFKnszqa44EQ7xoaT48nnO0lOyXEULfXMWi8krrjOPGYkeK30Okzla6ATbBYsyZ0ySinK0FVkpv3DwzfQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.0.tgz", + "integrity": "sha512-WOpkVxAjFd369iaIzEgNRreFD+gWdUMIGD5zplhNKNeqS6mm5dac3q2AFyCBmzYoAdouzZvRBgxy4z8QHZb4/A==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.0.tgz", + "integrity": "sha512-DRWw0mOHusrCCuw2rqP87oLg6PGlkomVDFqw2hIwsSfwWpu4k3XLcBPaKKl6ct/GtL/cwNkgwjV/tc0Mqht3VA==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.0.tgz", + "integrity": "sha512-9APy+nFWhHS+kzLgWZfLcyrUd7YqnAQVa4BPOo4xkoHpdoktOAPG4cEr9+Jpl0TtqfVmcMJimNL5qNTyyOHZNA==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.0.tgz", + "integrity": "sha512-y9RNUYDe2A1UAdhLyfeOodGRszQdaEoe4nfOpp/sNVPl2CWIcUyFaDoCh4vPLPxu19803j2naLqZup2WxDXCLA==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.0.tgz", + "integrity": "sha512-cC1wkC0Mlucd0KSiGrLkJnB/ZqPvZCntc/Lk7ZnYO5ZSbF2euNek4Xvxafojq+wN1q/W0eprdpUIjUr/EV2PBg==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.0.tgz", + "integrity": "sha512-LiYMhUZicB1QG//+RvmYZpXJO8fYRENfp+MZUCnG9aw+AKvGAy9gPaCnuwsPcBFs8EV66M0NNxj9VHcNklE8zw==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.1.tgz", + "integrity": "sha512-jygmR02PpCYypt7xB7nst1vqjZp/BpRA/Kf9nK7qRponJ/KrLPaZWEG4G15z1d2FZ6XqI+T0350ha3RSnKx24A==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.3.0" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.1.tgz", + "integrity": "sha512-ErCRyGU7LeoaFBZ0xW8hhLlXzhAg80sc4vxePB86qvtEvW1jEhhmbiNBP4oEzZfPMnu6HwHXfzD2W2kBU+RnCw==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.3.0" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.1.tgz", + "integrity": "sha512-LUWZ2+r2UoLCd8j0RLCwQ4gL6w47+Y7igxtVnPIDXOOEjV86LpBkAHq5VpJeg+GHbw0KN/JWlPJOdZjyZnFqFQ==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.3.0" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.1.tgz", + "integrity": "sha512-i7x6J3mwF4JgT0sM4V4WlAWdJ0bucPtA9rzO1bTji1n5qgBq/W5nn87RvOQPleuuxahNoLdTngByD8/vDDLArw==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.3.0" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.1.tgz", + "integrity": "sha512-0zSaTUjTF0kIWTSYxD4EG/nvCU4jez53+3RdURtoY3HvbXtIQ98W90JnrGz/oLRFuEnfIy9+7xeq883euc0ZWw==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.3.0" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.1.tgz", + "integrity": "sha512-NbJD4mWdeyrNQKluO/tR/wBDOelcowSVGNBWxI0e3ZtlXc6F/UOVKDj1MLD4zl3oHTuvKW3s+MA9N54YTldAYw==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.0" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.1.tgz", + "integrity": "sha512-VoW2sQCWI+0YIKQEmWJ8vzaQjTg9wIyfkFpvEfAS2h43X6iHu7GTk1hhOgB4IpSzCHe8UwQZIcx7b81VTaOrJA==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.3.0" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.1.tgz", + "integrity": "sha512-LjBoSd/c5JU0/K5MwzDMlgsSRP2bPn98JQGFFQAOLQ0bU/1z4ekxUdSKY9BmlwSh/cA+OrvpgsWqfZyYfVHBRw==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.3.0" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.1.tgz", + "integrity": "sha512-PCQUoQdZyE8tp3HpbevuihfUmgSP4qWI0FGEPWoeXqaS+cUrFfemabHQiebUmUmlUhCuNnQMxGrQ+CPqK4hnxg==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.0" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.1.tgz", + "integrity": "sha512-xU2ml2bU2OPxYVvW2A6ae4M1g5QKyhKG06P4FAt+YEaFQQO0919Qx+XxIZEUuWTMoDViLpMws2/dQwoe/VcA6A==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.1.tgz", + "integrity": "sha512-IkmHwuFhYpd3bTsN5SAahjwhiAcyXPooBt8vEUgxY3T0IP70sSJ0nU1xiPzZY8AH/OB1XpV3j8aZSVSOSfTbdA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.1.tgz", + "integrity": "sha512-wQahqCi9MD8Yxzg4gVM4fNrZxh+r6vD55PyIg+WJPaM5ZRUyF35iQpwJCuma3r6viU9/8Pxlc+XHV+woVa6nCQ==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.1.tgz", + "integrity": "sha512-WzBtkYtZHATLPe8XRharxZXxQ9cdLrQWHiwxt+BJ5rBsisQrKeeV86ErxPSVhcG6xCEuNhs0SqLpWr7XDa2k6w==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", + "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@moq/lite": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@moq/lite/-/lite-0.2.4.tgz", + "integrity": "sha512-pd8AJl10EWjJYxy+rJJYlSJF+DFYudkMc1xkPtUUBzWCqiP9WD254oJo09xamu4ATXrDycg5IDRazqcvQTjZcg==", + "deprecated": "Renamed to @moq/net. See https://moq.dev", + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "@moq/qmux": "^0.0.6", + "@moq/signals": "^0.1.6", + "async-mutex": "^0.5.0" + }, + "peerDependencies": { + "zod": "^4.0.0" + } + }, + "node_modules/@moq/net": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@moq/net/-/net-0.1.6.tgz", + "integrity": "sha512-hRhR1erz8L9fF+i8Bg6ymg4S/ITa3pjv8/Kq5HAnwR5p6h5PqeQs/6DCS0Zd45ibpRVKv1EliRaFJlOFUV4tFA==", + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "@moq/qmux": "^0.0.6", + "@moq/signals": "^0.1.10", + "async-mutex": "^0.5.0" + }, + "peerDependencies": { + "zod": "^4.0.0" + } + }, + "node_modules/@moq/qmux": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@moq/qmux/-/qmux-0.0.6.tgz", + "integrity": "sha512-ISuGz05lUvf1hzHW3Aw3VnsGRJe1w9Qdog3LQ66KS+l+5mzQsPANvW8yOioEe1Z9dJO2G3sAHoGPnzwnsY9SIQ==", + "license": "(MIT OR Apache-2.0)" + }, + "node_modules/@moq/signals": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/@moq/signals/-/signals-0.1.10.tgz", + "integrity": "sha512-TSnQwcaywn/gIwC3UpwZbqYnXm2Zo8KZO6Y043jPiPnbnSlFWJVZ9TVdGAvQqrXGiaNihdcioF/0IEDABsfovw==", + "license": "(MIT OR Apache-2.0)", + "peerDependencies": { + "@types/react": "^19.2.17", + "react": "^19.0.0", + "solid-js": "^1.9.13" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "solid-js": { + "optional": true + } + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz", + "integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodable/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, + "node_modules/@oxc-project/types": { + "version": "0.133.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", + "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, + "node_modules/@posthog/core": { + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.38.0.tgz", + "integrity": "sha512-QLrJh0hMVpEJXHNyiJR9YhvIO5tzLDedw4UHdvB+ub4fXHMtHCA8H44qgl11HWR3ajpjxtJ8hs3HyOGVF3Zc1w==", + "license": "MIT", + "dependencies": { + "@posthog/types": "^1.391.1" + } + }, + "node_modules/@posthog/types": { + "version": "1.391.1", + "resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.391.1.tgz", + "integrity": "sha512-ASwd7Nf4pViqdYRYaNRyPYRVKWa1CcHUAUWR0XeQJLGdNnsWACBwe0sSieb/cHnKsRXjRwO/23KIY83lm/Ccpw==", + "license": "MIT" + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", + "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", + "license": "BSD-3-Clause" + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", + "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", + "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", + "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", + "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", + "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", + "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", + "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", + "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", + "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", + "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", + "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", + "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", + "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", + "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", + "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sidvind/better-ajv-errors": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@sidvind/better-ajv-errors/-/better-ajv-errors-5.0.0.tgz", + "integrity": "sha512-FeI/V2KGtOaDX+r0akidCGYy79lVR4YnAqk1GFgZFuHADErCAEmtZL4+IdCAcDXHqfZsII3fs9DrfC1pIR+19w==", + "license": "Apache-2.0", + "dependencies": { + "kleur": "^4.1.0" + }, + "engines": { + "node": "^20.19 || ^22.12 || >= 24.0" + }, + "peerDependencies": { + "ajv": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@so-ric/colorspace": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", + "license": "MIT", + "dependencies": { + "color": "^5.0.2", + "text-hex": "1.0.x" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@swmansion/argent": { + "resolved": "packages/argent", + "link": true + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bunyan": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.11.tgz", + "integrity": "sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/bytes": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@types/bytes/-/bytes-3.1.5.tgz", + "integrity": "sha512-VgZkrJckypj85YxEsEavcMmmSOIzkUHqWmM4CCyia5dc54YwsXzJ5uT4fYxBQNEXx+oF1krlhgCbvfubXqZYsQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cookiejar": { + "version": "2.1.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@types/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-MIyNUZipBTbyUNnhvuXJTY7B6qNI78meck9Jbv3wk0OgNwRyOOVEKDutAkOs1snB/tx0FafyR6/SN4Ps0hZPeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/methods": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "26.0.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.1.tgz", + "integrity": "sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==", + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/@types/pngjs": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@types/pngjs/-/pngjs-6.0.5.tgz", + "integrity": "sha512-0k5eKfrA83JOZPppLtS2C7OUtyNAl2wKNxfyYl9Q5g9lPkgBl/9hNyAu6HuEH2J4XmIv2znEpkDd0SaZVxW6iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/qs": { + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/semver": { + "version": "7.7.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, + "node_modules/@types/superagent": { + "version": "8.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cookiejar": "^2.1.5", + "@types/methods": "^1.1.4", + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/supertest": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/methods": "^1.1.4", + "@types/superagent": "^8.1.0" + } + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.62.0.tgz", + "integrity": "sha512-o+mpz7EYiMzXoySXiKmzlabIvTVqUuK5yLrAedRPRDA0IpPFMUV1IXt6OqljIxX/kumN6EjUYp41Hqelh6p/Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.62.0", + "@typescript-eslint/type-utils": "8.62.0", + "@typescript-eslint/utils": "8.62.0", + "@typescript-eslint/visitor-keys": "8.62.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.62.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.62.0.tgz", + "integrity": "sha512-dzHeT2gySzZtLDsuqxU9AkYgIsQoHAHtRBpOqM+Ofzx1Bwrd2RcCjQJ+6iQbsHOIR6NS33bF2W1k3blN1zLDrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.62.0", + "@typescript-eslint/types": "8.62.0", + "@typescript-eslint/typescript-estree": "8.62.0", + "@typescript-eslint/visitor-keys": "8.62.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.62.0.tgz", + "integrity": "sha512-wexnCqiTg7BOGtbLDftYpRWlmLq4xfoMd7BKFR6Y75sZS3QmRKLdN3yWLhmIYgqMmP/OXWpj3H8odkb5nGURCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.62.0", + "@typescript-eslint/types": "^8.62.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.62.0.tgz", + "integrity": "sha512-1lX38kNxXIRb8mEc3lbq5mdHq1Pf2+U0nFU65KfT18mtPxxl0fvjuEE92mHuXPuCtElJhOrddOpyMlM3Z0umEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.62.0", + "@typescript-eslint/visitor-keys": "8.62.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.62.0.tgz", + "integrity": "sha512-y2GAdB6ykaXUvuspbYnizQc4oDDz0Tz/Yc7iWrXf9mx8vm/L/0vLHCe0tS2boG96Zy+DivnVDQ9ZUEWoHqqx1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.62.0.tgz", + "integrity": "sha512-+g5O3j0w2ldzC86Pv6fvbO/xhAonbJFIdf/MKQ1d30gndlsVzUOE83ldfSE15Qrl9fhFjK6AovHs5Wpp6vx86w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.62.0", + "@typescript-eslint/typescript-estree": "8.62.0", + "@typescript-eslint/utils": "8.62.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.62.0.tgz", + "integrity": "sha512-KvAclkktORPvM54TgLgA4z9HIV1M8zOgw9ZVNXl9f/8dLYfXYX1wkMXP7qmabpijQRV5bHJLOmoyGQbLMaUYeg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.62.0.tgz", + "integrity": "sha512-+hVbNxtW64pIcZWDPGbyaKF7vp2IBTVY5ma1blwwksrjdsbdqqEKvJWMGbBofei4F6Dovx1M0RJgoFeNu2279A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.62.0", + "@typescript-eslint/tsconfig-utils": "8.62.0", + "@typescript-eslint/types": "8.62.0", + "@typescript-eslint/visitor-keys": "8.62.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.62.0.tgz", + "integrity": "sha512-82r66fi9zYwZ+mTq3vKgwjbZ1PVk/DJzrXFLpG6RnBbdvH8TEGVHIs9H4d2drhkOzf0syZuD/OZvvlu6GDbP4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.62.0", + "@typescript-eslint/types": "8.62.0", + "@typescript-eslint/typescript-estree": "8.62.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.62.0.tgz", + "integrity": "sha512-CY3uyFSRbcQv3nnSv8S0+lDftMVz6P963PoRlxrV7ew/Md564g9ut60PYzdLM5qW4jFn93GBF+Soi90ISAN+GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.62.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", + "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.9", + "@vitest/utils": "4.1.9", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", + "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.9", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", + "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", + "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.9", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", + "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.9", + "@vitest/utils": "4.1.9", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", + "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", + "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.9", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.9.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.10.tgz", + "integrity": "sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==", + "license": "MIT", + "engines": { + "node": ">=14.6" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anynum": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz", + "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/appium": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/appium/-/appium-3.5.2.tgz", + "integrity": "sha512-Vi5W0XjehUaXbuAK7K7opi9iiObUK9FSADn9WDgFMe5pZ4rdaSo1vT9rXUOc1kxi4tEWEZz6r24rf4MWcL3zRw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@appium/base-driver": "10.7.1", + "@appium/base-plugin": "3.3.2", + "@appium/docutils": "2.5.1", + "@appium/logger": "2.0.9", + "@appium/schema": "1.2.1", + "@appium/support": "7.2.5", + "@appium/types": "1.5.1", + "@sidvind/better-ajv-errors": "5.0.0", + "ajv": "8.20.0", + "ajv-formats": "3.0.1", + "argparse": "2.0.1", + "asyncbox": "6.3.0", + "axios": "1.18.0", + "lilconfig": "3.1.3", + "lru-cache": "11.5.1", + "ora": "5.4.1", + "semver": "7.8.4", + "teen_process": "4.1.3", + "type-fest": "5.7.0", + "winston": "3.19.0", + "ws": "8.21.0", + "yaml": "2.9.0" + }, + "bin": { + "appium": "index.js" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + } + }, + "node_modules/appium-xcuitest-driver": { + "version": "11.17.4", + "resolved": "https://registry.npmjs.org/appium-xcuitest-driver/-/appium-xcuitest-driver-11.17.4.tgz", + "integrity": "sha512-qimirW88UpUXt4nchFrraRSkxIW9Vp+qVKO3Wlsag8dyJQX7G64GydXIrR8TCopzsKx4sHYtDk2GSGP9ZyJ27Q==", + "hasShrinkwrap": true, + "license": "Apache-2.0", + "dependencies": { + "@appium/css-locator-to-native": "^1.0.1", + "@appium/strongbox": "^1.0.0-rc.1", + "@colors/colors": "^1.6.0", + "appium-ios-device": "^3.1.12", + "appium-ios-simulator": "^8.2.0", + "appium-remote-debugger": "^15.7.3", + "appium-webdriveragent": "^15.0.0", + "appium-xcode": "^6.0.2", + "async-lock": "^1.4.0", + "asyncbox": "^6.3.0", + "axios": "^1.4.0", + "commander": "^14.0.1", + "dayjs": "^1.11.21", + "js2xmlparser2": "^0.x", + "lru-cache": "^11.1.0", + "node-devicectl": "^1.1.0", + "node-simctl": "^8.1.1", + "portscanner": "^2.2.0", + "semver": "^7.5.4", + "teen_process": "^4.0.4", + "winston": "^3.17.0", + "ws": "^8.13.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + }, + "optionalDependencies": { + "appium-ios-remotexpc": "^5.6.1" + }, + "peerDependencies": { + "appium": "^3.0.0-rc.2" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@appium/base-driver": { + "version": "10.7.1", + "resolved": "https://registry.npmjs.org/@appium/base-driver/-/base-driver-10.7.1.tgz", + "integrity": "sha512-DE6TcCBvoL5N+5zjbwhyVvnfzTvvu1ZFLULjK8MZuPQPklLc8nRpYIgKIqvsGDUp2QH29Gs0SQDqO9XebOX1Jw==", + "license": "Apache-2.0", + "dependencies": { + "@appium/support": "7.2.5", + "@appium/types": "1.5.1", + "async-lock": "1.4.1", + "asyncbox": "6.3.0", + "axios": "1.18.0", + "body-parser": "2.2.2", + "express": "5.2.1", + "fastest-levenshtein": "1.0.16", + "http-status-codes": "2.3.0", + "lru-cache": "11.5.1", + "method-override": "3.0.0", + "morgan": "1.11.0", + "path-to-regexp": "8.4.2", + "serve-favicon": "2.5.1", + "type-fest": "5.7.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + }, + "optionalDependencies": { + "spdy": "4.0.2" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@appium/base-driver/node_modules/asyncbox": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/asyncbox/-/asyncbox-6.3.0.tgz", + "integrity": "sha512-7IFpnQDltd5rYQjhIJIpyismJtdWmw/pOABZKJfv2WVo0a6iYh2ZzUuCJJclae5mBtK0H/EychxXg91GB7rGdQ==", + "license": "Apache-2.0", + "dependencies": { + "p-limit": "^7.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@appium/base-driver/node_modules/axios": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.0.tgz", + "integrity": "sha512-E32NzpYKp++W7XRe52rHiXV2ehxmh3wbdgO7MHeFM+vqxLBYHzt0ElkiImtOBxtOmyp0yoC8C6uESVV84Y2/hw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@appium/base-driver/node_modules/lru-cache": { + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@appium/css-locator-to-native": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@appium/css-locator-to-native/-/css-locator-to-native-1.0.5.tgz", + "integrity": "sha512-9CmPR0U7vAnKuHDX/Z57joXTlfRcWmFAclInsftSw2v+sspMUZokKTKxs198RAS0/+6a3U7734AfsJlE0EwO/A==", + "license": "Apache-2.0", + "dependencies": { + "css-selector-parser": "^3.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@appium/logger": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@appium/logger/-/logger-2.0.9.tgz", + "integrity": "sha512-3rNqtyxTygrDkxEMA2UB35EQfzxlnGCkIJLdIRDLKhVfbHgFw8XEnPnYuvMKvzG8zVeBUoh76dxKVHyIezPsIQ==", + "license": "ISC", + "dependencies": { + "lru-cache": "11.5.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@appium/logger/node_modules/lru-cache": { + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@appium/schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@appium/schema/-/schema-1.2.1.tgz", + "integrity": "sha512-+XmwihFeW10Q0UI/WdLsMTfnC2d2ec0ceGRdhRcfBuo5cClfm/E7VCJH2PE64KE0A17ZlsZf63FRkKfNHw9PKw==", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "0.4.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@appium/strongbox": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@appium/strongbox/-/strongbox-1.1.2.tgz", + "integrity": "sha512-u7+ZRrpipFYzQPFZVXTK1wlhSgc5KM7OBKWdkYgFqrWAyasCyxlg2E1A0OVVZUSAJWk5gZNLFToQaASVMmSZ8A==", + "license": "Apache-2.0", + "dependencies": { + "env-paths": "4.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@appium/support": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/@appium/support/-/support-7.2.5.tgz", + "integrity": "sha512-+sk7HdWYD5EMcSVtiMK09aA960suBJY/8d+r5v8j9vHw47R4ukLrM2lShpIpFFCTTfz5ZTCjCtvqNwwM8EqyMg==", + "license": "Apache-2.0", + "dependencies": { + "@appium/logger": "2.0.9", + "@appium/types": "1.5.1", + "archiver": "8.0.0", + "asyncbox": "6.3.0", + "axios": "1.18.0", + "bluebird": "3.7.2", + "bplist-creator": "0.1.1", + "bplist-parser": "0.3.2", + "form-data": "4.0.6", + "glob": "13.0.6", + "jsftp": "2.1.3", + "klaw": "4.1.0", + "lockfile": "1.0.4", + "normalize-package-data": "8.0.0", + "plist": "4.0.0", + "pluralize": "8.0.0", + "sanitize-filename": "1.6.4", + "semver": "7.8.4", + "shell-quote": "1.8.4", + "teen_process": "4.1.3", + "type-fest": "5.7.0", + "uuid": "14.0.0", + "which": "6.0.1", + "yauzl": "3.4.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + }, + "optionalDependencies": { + "sharp": "0.35.1" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@appium/support/node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.0.tgz", + "integrity": "sha512-y9RNUYDe2A1UAdhLyfeOodGRszQdaEoe4nfOpp/sNVPl2CWIcUyFaDoCh4vPLPxu19803j2naLqZup2WxDXCLA==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@appium/support/node_modules/@img/sharp-linux-x64": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.1.tgz", + "integrity": "sha512-NbJD4mWdeyrNQKluO/tR/wBDOelcowSVGNBWxI0e3ZtlXc6F/UOVKDj1MLD4zl3oHTuvKW3s+MA9N54YTldAYw==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@appium/support/node_modules/asyncbox": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/asyncbox/-/asyncbox-6.3.0.tgz", + "integrity": "sha512-7IFpnQDltd5rYQjhIJIpyismJtdWmw/pOABZKJfv2WVo0a6iYh2ZzUuCJJclae5mBtK0H/EychxXg91GB7rGdQ==", + "license": "Apache-2.0", + "dependencies": { + "p-limit": "^7.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@appium/support/node_modules/axios": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.0.tgz", + "integrity": "sha512-E32NzpYKp++W7XRe52rHiXV2ehxmh3wbdgO7MHeFM+vqxLBYHzt0ElkiImtOBxtOmyp0yoC8C6uESVV84Y2/hw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@appium/support/node_modules/semver": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@appium/support/node_modules/sharp": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.1.tgz", + "integrity": "sha512-lW979AMi+ESidzMv/Lnv+F9bknzLyxLqFI05Sm433vOeRcltgxQmXpnfOOFIAlKtwXU/ksupm2srQoFCkR214g==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.4" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.1", + "@img/sharp-darwin-x64": "0.35.1", + "@img/sharp-freebsd-wasm32": "0.35.1", + "@img/sharp-libvips-darwin-arm64": "1.3.0", + "@img/sharp-libvips-darwin-x64": "1.3.0", + "@img/sharp-libvips-linux-arm": "1.3.0", + "@img/sharp-libvips-linux-arm64": "1.3.0", + "@img/sharp-libvips-linux-ppc64": "1.3.0", + "@img/sharp-libvips-linux-riscv64": "1.3.0", + "@img/sharp-libvips-linux-s390x": "1.3.0", + "@img/sharp-libvips-linux-x64": "1.3.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.0", + "@img/sharp-libvips-linuxmusl-x64": "1.3.0", + "@img/sharp-linux-arm": "0.35.1", + "@img/sharp-linux-arm64": "0.35.1", + "@img/sharp-linux-ppc64": "0.35.1", + "@img/sharp-linux-riscv64": "0.35.1", + "@img/sharp-linux-s390x": "0.35.1", + "@img/sharp-linux-x64": "0.35.1", + "@img/sharp-linuxmusl-arm64": "0.35.1", + "@img/sharp-linuxmusl-x64": "0.35.1", + "@img/sharp-webcontainers-wasm32": "0.35.1", + "@img/sharp-win32-arm64": "0.35.1", + "@img/sharp-win32-ia32": "0.35.1", + "@img/sharp-win32-x64": "0.35.1" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@appium/support/node_modules/teen_process": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/teen_process/-/teen_process-4.1.3.tgz", + "integrity": "sha512-8W7Xp7WtJ5ZXjv0iHMsCgPPKzUt6ACfG/rDWX0tMIlMJaYcTYsPw3ZQQ9+hG7YsY+gm+DUATiyah3AraJ9JYpg==", + "license": "Apache-2.0", + "dependencies": { + "shell-quote": "^1.8.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@appium/types": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@appium/types/-/types-1.5.1.tgz", + "integrity": "sha512-cQkbSh6AwEGlowfKBxVngtBc5fkqAPmg4WdjGZ2txa81lpfuNTLX50VWM8Nxf3hiCgRMsRVfBWdU9x38OJqDrQ==", + "license": "Apache-2.0", + "dependencies": { + "@appium/schema": "1.2.1", + "type-fest": "5.7.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + }, + "peerDependencies": { + "@appium/logger": "^2.0.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@dabh/diagnostics": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", + "license": "MIT", + "dependencies": { + "@so-ric/colorspace": "^1.1.6", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@so-ric/colorspace": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", + "license": "MIT", + "dependencies": { + "color": "^5.0.2", + "text-hex": "1.0.x" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/@xmldom/xmldom": { + "version": "0.9.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.10.tgz", + "integrity": "sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==", + "license": "MIT", + "engines": { + "node": ">=14.6" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/appium-ios-device": { + "version": "3.1.17", + "resolved": "https://registry.npmjs.org/appium-ios-device/-/appium-ios-device-3.1.17.tgz", + "integrity": "sha512-L/isoRQ2NFUSFOyMMQ+n8V5/CXCVPZ/049sFA4VkVhqPbQSljkoZtJL6vVOG5vCg3wwKFzneObEDMeRrIM7a1w==", + "license": "Apache-2.0", + "dependencies": { + "@appium/support": "^7.2.2", + "asyncbox": "^6.0.1", + "axios": "^1.16.0", + "bplist-creator": "^0.x", + "bplist-parser": "^0.x", + "lodash": "^4.17.15", + "semver": "^7.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/appium-ios-remotexpc": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/appium-ios-remotexpc/-/appium-ios-remotexpc-5.11.0.tgz", + "integrity": "sha512-BCFqeDexoxy4zQfNYr6kQFV2wh+sBMp+ScoQw4Q7RdRg8DwYGRLwXVOR5Qq44RuqR8scYy/52cLq7c6Vvin4Cw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@appium/strongbox": "^1.1.2", + "@appium/support": "^7.2.2", + "@xmldom/xmldom": "^0.x", + "appium-ios-tuntap": "^1.0.0", + "async-lock": "^1.4.1", + "axios": "^1.15.1", + "commander": "^14.0.1", + "minimatch": "^10.1.1", + "node-devicectl": "^1.2.0", + "npm-run-all2": "^8.0.4", + "path-to-regexp": "^8.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/appium-ios-simulator": { + "version": "8.2.7", + "resolved": "https://registry.npmjs.org/appium-ios-simulator/-/appium-ios-simulator-8.2.7.tgz", + "integrity": "sha512-Zw1iZKvoqIFSbPQdu/935VDDiz0xmi0kotA57lzwolG5FhvV/zA1n9zGavBByq2C5mkYxwkRYYEN+Hxv6RmPIQ==", + "license": "Apache-2.0", + "dependencies": { + "@appium/support": "^7.2.5", + "@xmldom/xmldom": "^0.x", + "appium-xcode": "^6.0.0", + "async-lock": "^1.0.0", + "asyncbox": "^6.0.1", + "node-simctl": "^8.1.1", + "semver": "^7.0.0", + "teen_process": "^4.0.4" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/appium-ios-tuntap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/appium-ios-tuntap/-/appium-ios-tuntap-1.1.0.tgz", + "integrity": "sha512-8s0fUNm7OvCsrZucE+QkpaOJUCGSw5HFkDremKXGt3kJJGVhBJY04Pc6p91NFmOCqgU+K8mKi6RvfLlJFJ9fcw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@appium/support": "^7.2.0", + "node-addon-api": "^8.5.0", + "node-gyp-build": "^4.8.4", + "typescript": "^6.0.2" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/appium-remote-debugger": { + "version": "15.10.8", + "resolved": "https://registry.npmjs.org/appium-remote-debugger/-/appium-remote-debugger-15.10.8.tgz", + "integrity": "sha512-hc+g5Ad0BZRtHXgVQAp3RRpU61yH2G2JYBqPLnqmLf3KzYpipL5xD8II7ebblfLMx6wKToEQcbqxRff0m9vlhA==", + "license": "Apache-2.0", + "dependencies": { + "@appium/base-driver": "^10.0.0-rc.1", + "@appium/support": "^7.2.2", + "appium-ios-device": "^3.0.0", + "async-lock": "^1.4.1", + "asyncbox": "^6.3.0", + "glob": "^13.0.0", + "teen_process": "^4.0.4" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + }, + "optionalDependencies": { + "appium-ios-remotexpc": "^5.0.1" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/appium-webdriveragent": { + "version": "15.1.4", + "resolved": "https://registry.npmjs.org/appium-webdriveragent/-/appium-webdriveragent-15.1.4.tgz", + "integrity": "sha512-1tPVzIVPsBKynbTFqJyk3Hrf/FZ6kDmeP81P24hJ6q3gYHd2ljsI6OYEhINSbzxDdDmgTuWyYoUa1YtFvZC8oA==", + "license": "Apache-2.0", + "dependencies": { + "@appium/base-driver": "^10.3.0", + "@appium/strongbox": "^1.0.0-rc.1", + "@appium/support": "^7.2.1", + "appium-ios-simulator": "^8.0.0", + "async-lock": "^1.0.0", + "asyncbox": "^6.1.0", + "axios": "^1.16.0", + "teen_process": "^4.0.7" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/appium-xcode": { + "version": "6.2.6", + "resolved": "https://registry.npmjs.org/appium-xcode/-/appium-xcode-6.2.6.tgz", + "integrity": "sha512-kcAh0TU0SbzUAHrRoQxSXlsH/3mL38ffgz0iyWtsOvJfpLQ76KQ9sGDWdl5LJh1r/s4jEj0GS0D9nK7l+IWodA==", + "license": "Apache-2.0", + "dependencies": { + "@appium/support": "^7.2.2", + "asyncbox": "^6.0.1", + "semver": "^7.0.0", + "teen_process": "^4.0.4" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/archiver": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-8.0.0.tgz", + "integrity": "sha512-fV1orZfsnPn9BaSByR/qE67rJCLJEy2Ox5bq7nJh+jquWaNh6Sfec75kJ2T6PtdGUbPQlrVoSVCEOa5SdiTQ1g==", + "license": "MIT", + "dependencies": { + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "is-stream": "^4.0.0", + "lazystream": "^1.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^3.0.0", + "tar-stream": "^3.0.0", + "zip-stream": "^7.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/async-lock": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/asyncbox": { + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/asyncbox/-/asyncbox-6.3.4.tgz", + "integrity": "sha512-WjCWd8lgZG/HowtEFrnQpEHn/qLRXZTyqIlhHbLeqUyoo/VbcXQPUZM7+dXcd14ws6SF9EhnokZ/5HJ8Wm+eww==", + "license": "Apache-2.0", + "dependencies": { + "p-limit": "^7.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/axios": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz", + "integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/b4a": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", + "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/appium-xcuitest-driver/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/bare-events": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz", + "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==", + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/appium-xcuitest-driver/node_modules/bare-fs": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.4.tgz", + "integrity": "sha512-y1kC+ffIx/tPLdTE693uNjHfzTfr+ravR5tvWlMXe25nELbkqV400S71qHDwbkAQ1FVEZobB1NFRzFbCCcyBCQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/appium-xcuitest-driver/node_modules/bare-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.1.tgz", + "integrity": "sha512-JprUlveX3QjApC1cTpsUOiscADftCGVWkzitbHsRqv84hzYwYHw2mbluddsq5TvI8mH/8Ov1f4BiMAdcB0oYnQ==", + "license": "Apache-2.0" + }, + "node_modules/appium-xcuitest-driver/node_modules/bare-stream": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.3.tgz", + "integrity": "sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.8.1", + "streamx": "^2.25.0", + "teex": "^1.0.1" + }, + "peerDependencies": { + "bare-abort-controller": "*", + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + }, + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/appium-xcuitest-driver/node_modules/bare-url": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.5.tgz", + "integrity": "sha512-K+y9xF1tN+CdPu4qWwr0QiK1Al07eFPGYK5M2pDXcmHdMdgC/tT/bpmMe1hrmRHaidKLkXrC+cRNYf3XVDUhSQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/bplist-creator": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.1.tgz", + "integrity": "sha512-Ese7052fdWrxp/vqSJkydgx/1MdBnNOCV2XVfbmdGWD2H6EYza+Q4pyYSuVSnCUD22hfI/BFI4jHaC3NLXLlJQ==", + "license": "MIT", + "dependencies": { + "stream-buffers": "2.2.x" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/bplist-parser": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.2.tgz", + "integrity": "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==", + "license": "MIT", + "dependencies": { + "big-integer": "1.6.x" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/brace-expansion": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", + "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/color": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", + "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", + "license": "MIT", + "dependencies": { + "color-convert": "^3.1.3", + "color-string": "^2.1.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/color-convert": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", + "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=14.6" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/color-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/color-string": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", + "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/compress-commons": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-7.0.1.tgz", + "integrity": "sha512-g0S8KAD8qf4+V//pr3BfB1aBnARLXNz2Gx+jmHU0LEriUuoQUOPOulVquHKTJ8+EAIIO7fhseNDr9wK5Q9FKBQ==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^7.0.1", + "is-stream": "^4.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/crc32-stream": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-7.0.1.tgz", + "integrity": "sha512-IBWsY8xznyQrcHn8h4bC8/4ErNke5elzgG8GcqF4RFPw6aHkWWRc7Tgw6upjaTX/CT/yQgqYENkxYsTYN+hW2g==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "optional": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC", + "optional": true + }, + "node_modules/appium-xcuitest-driver/node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "optional": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/css-selector-parser": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-3.3.0.tgz", + "integrity": "sha512-Y2asgMGFqJKF4fq4xHDSlFYIkeVfRsm69lQC1q9kbEsH5XtnINTMrweLkjYMeaUgiXBy/uvKeO/a1JHTNnmB2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/dayjs": { + "version": "1.11.21", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz", + "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/appium-xcuitest-driver/node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "license": "MIT", + "optional": true + }, + "node_modules/appium-xcuitest-driver/node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/env-paths": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-4.0.0.tgz", + "integrity": "sha512-pxP8eL2SwwaTRi/KHYwLYXinDs7gL3jxFcBYmEdYfZmZXbaVDvdppd0XBU8qVz03rDfKZMXg1omHCbsJjZrMsw==", + "license": "MIT", + "dependencies": { + "is-safe-filename": "^0.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/appium-xcuitest-driver/node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/ftp-response-parser": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ftp-response-parser/-/ftp-response-parser-1.0.1.tgz", + "integrity": "sha512-++Ahlo2hs/IC7UVQzjcSAfeUpCwTTzs4uvG5XfGnsinIFkWUYF4xWwPd5qZuK8MJrmUIxFMuHcfqaosCDjvIWw==", + "dependencies": { + "readable-stream": "^1.0.31" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/ftp-response-parser/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/ftp-response-parser/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/ftp-response-parser/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "license": "MIT", + "optional": true + }, + "node_modules/appium-xcuitest-driver/node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/hosted-git-info": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.3.tgz", + "integrity": "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==", + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "optional": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "optional": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "license": "MIT", + "optional": true + }, + "node_modules/appium-xcuitest-driver/node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/http-status-codes": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", + "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/appium-xcuitest-driver/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/appium-xcuitest-driver/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/is-number-like": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/is-number-like/-/is-number-like-1.0.8.tgz", + "integrity": "sha512-6rZi3ezCyFcn5L71ywzz2bS5b2Igl1En3eTlZlvKjpz1n3IZLAYMbKYAIQgFmEu0GENg92ziU/faEOA/aixjbA==", + "license": "ISC", + "dependencies": { + "lodash.isfinite": "^3.3.2" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/is-safe-filename": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-safe-filename/-/is-safe-filename-0.1.1.tgz", + "integrity": "sha512-4SrR7AdnY11LHfDKTZY1u6Ga3RuxZdl3YKWWShO5iyuG5h8QS4GD2tOb04peBJ5I7pXbR+CGBNEhTcwK+FzN3g==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/js2xmlparser2": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/js2xmlparser2/-/js2xmlparser2-0.2.0.tgz", + "integrity": "sha512-SzFGc1hQqzpDcalKmrM5gobSMGRSRg2lgaZrHGIfowrmd8+uaI+PWW62jcCGIqI+b4wdyYK0VKMhvVtJfkD0cg==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/jsftp": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/jsftp/-/jsftp-2.1.3.tgz", + "integrity": "sha512-r79EVB8jaNAZbq8hvanL8e8JGu2ZNr2bXdHC4ZdQhRImpSPpnWwm5DYVzQ5QxJmtGtKhNNuvqGgbNaFl604fEQ==", + "license": "MIT", + "dependencies": { + "debug": "^3.1.0", + "ftp-response-parser": "^1.0.1", + "once": "^1.4.0", + "parse-listing": "^1.1.3", + "stream-combiner": "^0.2.2", + "unorm": "^1.4.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/jsftp/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", + "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", + "license": "MIT", + "optional": true, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/appium-xcuitest-driver/node_modules/klaw": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-4.1.0.tgz", + "integrity": "sha512-1zGZ9MF9H22UnkpVeuaGKOjfA2t6WrfdrJmGjy16ykcjnKQDmHVX+KI477rpbGevz/5FD4MC3xf1oxylBgcaQw==", + "license": "MIT", + "engines": { + "node": ">=14.14.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/lockfile": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.4.tgz", + "integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==", + "license": "ISC", + "dependencies": { + "signal-exit": "^3.0.2" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/lodash.isfinite": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz", + "integrity": "sha512-7FGG40uhC8Mm633uKW1r58aElFlBlxCrg9JfSi3P6aYiWmfiWF0PgMd86ZUsxE5GwWPdHoS2+48bwTh2VPkIQA==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "optional": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/method-override": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", + "integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==", + "license": "MIT", + "dependencies": { + "debug": "3.1.0", + "methods": "~1.1.2", + "parseurl": "~1.3.2", + "vary": "~1.1.2" }, "engines": { - "node": ">=18.0.0" + "node": ">= 0.10" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/method-override/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/method-override/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, "engines": { - "node": ">=6.0.0" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "dev": true, - "license": "MIT" + "node_modules/appium-xcuitest-driver/node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC", + "optional": true }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "dev": true, - "license": "MIT", + "node_modules/appium-xcuitest-driver/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@modelcontextprotocol/sdk": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", - "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", + "node_modules/appium-xcuitest-driver/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/morgan": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.11.0.tgz", + "integrity": "sha512-zSkVu3t18r39pw4ixfBKvfZi3y2UOqr7d4WYwcj3m8nXpEQK4rPO6GLzs/CExoRgmX3y9EjmmcXqv6jq0SK46g==", "license": "MIT", "dependencies": { - "@hono/node-server": "^1.19.9", - "ajv": "^8.17.1", - "ajv-formats": "^3.0.1", - "content-type": "^1.0.5", - "cors": "^2.8.5", - "cross-spawn": "^7.0.5", - "eventsource": "^3.0.2", - "eventsource-parser": "^3.0.0", - "express": "^5.2.1", - "express-rate-limit": "^8.2.1", - "hono": "^4.11.4", - "jose": "^6.1.3", - "json-schema-typed": "^8.0.2", - "pkce-challenge": "^5.0.0", - "raw-body": "^3.0.0", - "zod": "^3.25 || ^4.0", - "zod-to-json-schema": "^3.25.1" + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.4.1", + "on-headers": "~1.1.0" }, "engines": { - "node": ">=18" + "node": ">= 0.8.0" }, - "peerDependencies": { - "@cfworker/json-schema": "^4.1.1", - "zod": "^3.25 || ^4.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/morgan/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/morgan/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/node-addon-api": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.9.0.tgz", + "integrity": "sha512-ekZMeaaIzSQTSpr7X2X3iJM7lTzgnx8ahAG9pJfT/7+14mlEM8ZYQ9cgCDvSSRbReFK0oHli3WrZdCiRsgAT9Q==", + "license": "MIT", + "optional": true, + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/node-devicectl": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/node-devicectl/-/node-devicectl-1.4.3.tgz", + "integrity": "sha512-AnyroE1FzGhQd5oJ9vo/IJl37XypMFFkTEU7PFcrSDpbJo0KCXdLd5twbgZyUyqtma1KgK7VuE6NsIoMpaBfGA==", + "license": "Apache-2.0", + "dependencies": { + "@appium/logger": "^2.0.0-rc.1", + "teen_process": "^4.1.0" }, - "peerDependenciesMeta": { - "@cfworker/json-schema": { - "optional": true - }, - "zod": { - "optional": false - } + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" } }, - "node_modules/@moq/lite": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@moq/lite/-/lite-0.2.4.tgz", - "integrity": "sha512-pd8AJl10EWjJYxy+rJJYlSJF+DFYudkMc1xkPtUUBzWCqiP9WD254oJo09xamu4ATXrDycg5IDRazqcvQTjZcg==", - "deprecated": "Renamed to @moq/net. See https://moq.dev", - "license": "(MIT OR Apache-2.0)", + "node_modules/appium-xcuitest-driver/node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/node-simctl": { + "version": "8.2.8", + "resolved": "https://registry.npmjs.org/node-simctl/-/node-simctl-8.2.8.tgz", + "integrity": "sha512-/9op5PF56edvu5g7ns2/y8nA1qem9QNcKN0BXznd9AIwCwnyR7K2PjBCPZTrp5ZYjYVebsrZnzTXNLT28EhjRQ==", + "license": "Apache-2.0", "dependencies": { - "@moq/qmux": "^0.0.6", - "@moq/signals": "^0.1.6", - "async-mutex": "^0.5.0" + "@appium/logger": "^2.0.0-rc.1", + "asyncbox": "^6.0.1", + "rimraf": "^6.0.1", + "semver": "^7.0.0", + "teen_process": "^4.0.4", + "which": "^6.0.0" }, - "peerDependencies": { - "zod": "^4.0.0" + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" } }, - "node_modules/@moq/net": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@moq/net/-/net-0.1.6.tgz", - "integrity": "sha512-hRhR1erz8L9fF+i8Bg6ymg4S/ITa3pjv8/Kq5HAnwR5p6h5PqeQs/6DCS0Zd45ibpRVKv1EliRaFJlOFUV4tFA==", - "license": "(MIT OR Apache-2.0)", + "node_modules/appium-xcuitest-driver/node_modules/normalize-package-data": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-8.0.0.tgz", + "integrity": "sha512-RWk+PI433eESQ7ounYxIp67CYuVsS1uYSonX3kA6ps/3LWfjVQa/ptEg6Y3T6uAMq1mWpX9PQ+qx+QaHpsc7gQ==", + "license": "BSD-2-Clause", "dependencies": { - "@moq/qmux": "^0.0.6", - "@moq/signals": "^0.1.10", - "async-mutex": "^0.5.0" + "hosted-git-info": "^9.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" }, - "peerDependencies": { - "zod": "^4.0.0" + "engines": { + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@moq/qmux": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/@moq/qmux/-/qmux-0.0.6.tgz", - "integrity": "sha512-ISuGz05lUvf1hzHW3Aw3VnsGRJe1w9Qdog3LQ66KS+l+5mzQsPANvW8yOioEe1Z9dJO2G3sAHoGPnzwnsY9SIQ==", - "license": "(MIT OR Apache-2.0)" + "node_modules/appium-xcuitest-driver/node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/@moq/signals": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/@moq/signals/-/signals-0.1.10.tgz", - "integrity": "sha512-TSnQwcaywn/gIwC3UpwZbqYnXm2Zo8KZO6Y043jPiPnbnSlFWJVZ9TVdGAvQqrXGiaNihdcioF/0IEDABsfovw==", - "license": "(MIT OR Apache-2.0)", - "peerDependencies": { - "@types/react": "^19.2.17", - "react": "^19.0.0", - "solid-js": "^1.9.13" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "react": { - "optional": true - }, - "solid-js": { - "optional": true - } + "node_modules/appium-xcuitest-driver/node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", + "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", + "license": "ISC", + "optional": true, + "engines": { + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz", - "integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/npm-run-all2": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-8.0.4.tgz", + "integrity": "sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA==", "license": "MIT", "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.2" + "ansi-styles": "^6.2.1", + "cross-spawn": "^7.0.6", + "memorystream": "^0.3.1", + "picomatch": "^4.0.2", + "pidtree": "^0.6.0", + "read-package-json-fast": "^4.0.0", + "shell-quote": "^1.7.3", + "which": "^5.0.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "npm-run-all2": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" + "engines": { + "node": "^20.5.0 || >=22.0.0", + "npm": ">= 10" } }, - "node_modules/@noble/hashes": { - "version": "1.8.0", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/npm-run-all2/node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "license": "BlueOak-1.0.0", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/npm-run-all2/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", "engines": { - "node": "^14.21.3 || >=16" + "node": ">= 0.4" }, "funding": { - "url": "https://paulmillr.com/funding/" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@nodable/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/nodable" - } - ], - "license": "MIT" + "node_modules/appium-xcuitest-driver/node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "license": "MIT", + "optional": true }, - "node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" } }, - "node_modules/@paralleldrive/cuid2": { - "version": "2.3.1", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", "dependencies": { - "@noble/hashes": "^1.1.5" + "wrappy": "1" } }, - "node_modules/@posthog/core": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.38.0.tgz", - "integrity": "sha512-QLrJh0hMVpEJXHNyiJR9YhvIO5tzLDedw4UHdvB+ub4fXHMtHCA8H44qgl11HWR3ajpjxtJ8hs3HyOGVF3Zc1w==", + "node_modules/appium-xcuitest-driver/node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", "license": "MIT", "dependencies": { - "@posthog/types": "^1.391.1" + "fn.name": "1.x.x" } }, - "node_modules/@posthog/types": { - "version": "1.391.1", - "resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.391.1.tgz", - "integrity": "sha512-ASwd7Nf4pViqdYRYaNRyPYRVKWa1CcHUAUWR0XeQJLGdNnsWACBwe0sSieb/cHnKsRXjRwO/23KIY83lm/Ccpw==", - "license": "MIT" + "node_modules/appium-xcuitest-driver/node_modules/p-limit": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.0.tgz", + "integrity": "sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.2.1" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "license": "BSD-3-Clause" + "node_modules/appium-xcuitest-driver/node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "license": "BSD-3-Clause" + "node_modules/appium-xcuitest-driver/node_modules/parse-listing": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/parse-listing/-/parse-listing-1.1.3.tgz", + "integrity": "sha512-a1p1i+9Qyc8pJNwdrSvW1g5TPxRH0sywVi6OzVvYHRo6xwF9bDWBxtH0KkxeOOvhUE8vAMtiSfsYQFOuK901eA==", + "engines": { + "node": ">=0.6.21" + } }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", - "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", - "license": "BSD-3-Clause" + "node_modules/appium-xcuitest-driver/node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", - "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", - "license": "BSD-3-Clause" + "node_modules/appium-xcuitest-driver/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", - "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", - "license": "BSD-3-Clause", + "node_modules/appium-xcuitest-driver/node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", "dependencies": { - "@protobufjs/aspromise": "^1.1.1" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "license": "BSD-3-Clause" + "node_modules/appium-xcuitest-driver/node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", - "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", - "license": "BSD-3-Clause" + "node_modules/appium-xcuitest-driver/node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "license": "MIT" }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", - "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "license": "MIT", "optional": true, - "os": [ - "android" - ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", - "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/pidtree": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.1.tgz", + "integrity": "sha512-e0F9AOF1JMrCfBsyJOwU9lNvQ0WtXTq0j/4jk0BQ5JSI9VAybPXmDpPRw/2FQ3e5d3ZFN1mLh7jW99m/jjaptw==", "license": "MIT", "optional": true, - "os": [ - "darwin" - ], + "bin": { + "pidtree": "bin/pidtree.js" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=0.10" } }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", - "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/plist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-4.0.0.tgz", + "integrity": "sha512-4dOqNo0Y2NpfSf9q4+zr4bh7pzNWeckIam34Z0KYJhg8qtNNfh59VbD+Yna5SjwcxawVvLKx5w5FtuCijpEF4Q==", "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "@xmldom/xmldom": "^0.9.10", + "xmlbuilder": "^15.1.1" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", - "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=4" } }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", - "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", - "cpu": [ - "arm" - ], - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/portscanner": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.2.0.tgz", + "integrity": "sha512-IFroCz/59Lqa2uBvzK3bKDbDDIEaAY8XJ1jFxcLWTqosrsc32//P4VuSB2vZXoHiHqOmx8B5L5hnKOxL/7FlPw==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "async": "^2.6.0", + "is-number-like": "^1.0.3" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=0.4", + "npm": ">=1.0.0" } }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", - "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/portscanner/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">= 0.6.0" } }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", - "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">= 0.10" } }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", - "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", - "cpu": [ - "ppc64" - ], - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/qs": { + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/range-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", - "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", - "cpu": [ - "s390x" - ], - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">= 0.10" } }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", - "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", + "node_modules/appium-xcuitest-driver/node_modules/read-package-json-fast": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz", + "integrity": "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==", + "license": "ISC", "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", - "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", - "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", - "cpu": [ - "arm64" + "node_modules/appium-xcuitest-driver/node_modules/readable-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } ], - "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/readdir-glob": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-3.0.0.tgz", + "integrity": "sha512-AhNB2KgKeVJr16nK9LLZbJNWnYoT23ZrumNKFDebHBdkC8KHSqWo871JAUhoWC/RtjEVdqNMFpM6qrwRbaUqpw==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^10.2.2" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/yqnn" } }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", - "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, + "node_modules/appium-xcuitest-driver/node_modules/rimraf": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", + "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", + "license": "BlueOak-1.0.0", "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "glob": "^13.0.3", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", - "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">= 18" } }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", - "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", "license": "MIT", - "optional": true, - "os": [ - "win32" - ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=10" } }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", - "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "dev": true, - "license": "MIT" + "node_modules/appium-xcuitest-driver/node_modules/sanitize-filename": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.4.tgz", + "integrity": "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg==", + "license": "WTFPL OR ISC", + "dependencies": { + "truncate-utf8-bytes": "^1.0.0" + } }, - "node_modules/@swmansion/argent": { - "resolved": "packages/argent", - "link": true + "node_modules/appium-xcuitest-driver/node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "license": "MIT", + "optional": true }, - "node_modules/@tsconfig/node10": { - "version": "1.0.12", - "dev": true, - "license": "MIT" + "node_modules/appium-xcuitest-driver/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "dev": true, - "license": "MIT" + "node_modules/appium-xcuitest-driver/node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "dev": true, - "license": "MIT" + "node_modules/appium-xcuitest-driver/node_modules/serve-favicon": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.1.tgz", + "integrity": "sha512-JndLBslCLA/ebr7rS3d+/EKkzTsTi1jI2T9l+vHfAaGJ7A7NhtDpSZ0lx81HCNWnnE0yHncG+SSnVf9IMxOwXQ==", + "license": "MIT", + "dependencies": { + "etag": "~1.8.1", + "fresh": "~0.5.2", + "ms": "~2.1.3", + "parseurl": "~1.3.2", + "safe-buffer": "~5.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/serve-favicon/node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/serve-favicon/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT" }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", - "optional": true, "dependencies": { - "tslib": "^2.4.0" + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/@types/body-parser": { - "version": "1.19.6", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/appium-xcuitest-driver/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "license": "MIT", + "optional": true, "dependencies": { - "@types/connect": "*", - "@types/node": "*" + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@types/bunyan": { - "version": "1.8.11", - "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.11.tgz", - "integrity": "sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ==", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "license": "MIT", - "dependencies": { - "@types/node": "*" + "optional": true, + "engines": { + "node": ">=8" } }, - "node_modules/@types/bytes": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@types/bytes/-/bytes-3.1.5.tgz", - "integrity": "sha512-VgZkrJckypj85YxEsEavcMmmSOIzkUHqWmM4CCyia5dc54YwsXzJ5uT4fYxBQNEXx+oF1krlhgCbvfubXqZYsQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/chai": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", - "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/shell-quote": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.4.tgz", + "integrity": "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==", "license": "MIT", - "dependencies": { - "@types/deep-eql": "*", - "assertion-error": "^2.0.1" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@types/connect": { - "version": "3.4.38", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "license": "MIT", "dependencies": { - "@types/node": "*" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@types/cookiejar": { - "version": "2.1.5", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/debug": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", - "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "node_modules/appium-xcuitest-driver/node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "license": "MIT", "dependencies": { - "@types/ms": "*" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/esrecurse": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", - "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/express": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", - "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "^2" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@types/express-serve-static-core": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", - "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "dev": true, - "license": "MIT" + "node_modules/appium-xcuitest-driver/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" }, - "node_modules/@types/ini": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@types/ini/-/ini-4.1.1.tgz", - "integrity": "sha512-MIyNUZipBTbyUNnhvuXJTY7B6qNI78meck9Jbv3wk0OgNwRyOOVEKDutAkOs1snB/tx0FafyR6/SN4Ps0hZPeg==", - "dev": true, - "license": "MIT" + "node_modules/appium-xcuitest-driver/node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" + "node_modules/appium-xcuitest-driver/node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } }, - "node_modules/@types/methods": { - "version": "1.1.4", - "dev": true, - "license": "MIT" + "node_modules/appium-xcuitest-driver/node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "license": "CC-BY-3.0" }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT" + "node_modules/appium-xcuitest-driver/node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "license": "CC0-1.0" }, - "node_modules/@types/node": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.1.tgz", - "integrity": "sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==", + "node_modules/appium-xcuitest-driver/node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", "license": "MIT", + "optional": true, "dependencies": { - "undici-types": "~8.3.0" + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@types/pngjs": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/@types/pngjs/-/pngjs-6.0.5.tgz", - "integrity": "sha512-0k5eKfrA83JOZPppLtS2C7OUtyNAl2wKNxfyYl9Q5g9lPkgBl/9hNyAu6HuEH2J4XmIv2znEpkDd0SaZVxW6iQ==", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", "license": "MIT", + "optional": true, "dependencies": { - "@types/node": "*" + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" } }, - "node_modules/@types/qs": { - "version": "6.15.1", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", - "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" + "node_modules/appium-xcuitest-driver/node_modules/spdy-transport/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } }, - "node_modules/@types/semver": { - "version": "7.7.1", - "dev": true, - "license": "MIT" + "node_modules/appium-xcuitest-driver/node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "license": "MIT", + "engines": { + "node": "*" + } }, - "node_modules/@types/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", - "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", - "dependencies": { - "@types/node": "*" + "engines": { + "node": ">= 0.8" } }, - "node_modules/@types/serve-static": { + "node_modules/appium-xcuitest-driver/node_modules/stream-buffers": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*" + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", + "integrity": "sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==", + "license": "Unlicense", + "engines": { + "node": ">= 0.10.0" } }, - "node_modules/@types/superagent": { - "version": "8.1.9", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/stream-combiner": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", + "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==", "license": "MIT", "dependencies": { - "@types/cookiejar": "^2.1.5", - "@types/methods": "^1.1.4", - "@types/node": "*", - "form-data": "^4.0.0" + "duplexer": "~0.1.1", + "through": "~2.3.4" } }, - "node_modules/@types/supertest": { - "version": "7.2.0", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/streamx": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz", + "integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==", "license": "MIT", "dependencies": { - "@types/methods": "^1.1.4", - "@types/superagent": "^8.1.0" + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" } }, - "node_modules/@types/ws": { - "version": "8.18.1", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "license": "MIT", "dependencies": { - "@types/node": "*" + "safe-buffer": "~5.2.0" } }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.62.0.tgz", - "integrity": "sha512-o+mpz7EYiMzXoySXiKmzlabIvTVqUuK5yLrAedRPRDA0IpPFMUV1IXt6OqljIxX/kumN6EjUYp41Hqelh6p/Dw==", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/tagged-tag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", + "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.62.0", - "@typescript-eslint/type-utils": "8.62.0", - "@typescript-eslint/utils": "8.62.0", - "@typescript-eslint/visitor-keys": "8.62.0", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.5.0" - }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=20" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.62.0", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/tar-stream": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.0.tgz", + "integrity": "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==", "license": "MIT", - "engines": { - "node": ">= 4" + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" } }, - "node_modules/@typescript-eslint/parser": { - "version": "8.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.62.0.tgz", - "integrity": "sha512-dzHeT2gySzZtLDsuqxU9AkYgIsQoHAHtRBpOqM+Ofzx1Bwrd2RcCjQJ+6iQbsHOIR6NS33bF2W1k3blN1zLDrA==", - "dev": true, - "license": "MIT", + "node_modules/appium-xcuitest-driver/node_modules/teen_process": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/teen_process/-/teen_process-4.1.7.tgz", + "integrity": "sha512-rWYaZsS8hjySi6lgrewZsOtELuya7W2xu2DEG16ibcDcH+iyqn4fyp8Y08IncBo/q8MdOibVyP9GViqzvA7nsw==", + "license": "Apache-2.0", "dependencies": { - "@typescript-eslint/scope-manager": "8.62.0", - "@typescript-eslint/types": "8.62.0", - "@typescript-eslint/typescript-estree": "8.62.0", - "@typescript-eslint/visitor-keys": "8.62.0", - "debug": "^4.4.3" + "shell-quote": "^1.8.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" } }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.62.0.tgz", - "integrity": "sha512-wexnCqiTg7BOGtbLDftYpRWlmLq4xfoMd7BKFR6Y75sZS3QmRKLdN3yWLhmIYgqMmP/OXWpj3H8odkb5nGURCQ==", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.62.0", - "@typescript-eslint/types": "^8.62.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "streamx": "^2.12.5" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.62.0.tgz", - "integrity": "sha512-1lX38kNxXIRb8mEc3lbq5mdHq1Pf2+U0nFU65KfT18mtPxxl0fvjuEE92mHuXPuCtElJhOrddOpyMlM3Z0umEA==", - "dev": true, - "license": "MIT", + "node_modules/appium-xcuitest-driver/node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "license": "Apache-2.0", "dependencies": { - "@typescript-eslint/types": "8.62.0", - "@typescript-eslint/visitor-keys": "8.62.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "b4a": "^1.6.4" } }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.62.0.tgz", - "integrity": "sha512-y2GAdB6ykaXUvuspbYnizQc4oDDz0Tz/Yc7iWrXf9mx8vm/L/0vLHCe0tS2boG96Zy+DivnVDQ9ZUEWoHqqx1g==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } + "node_modules/appium-xcuitest-driver/node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "license": "MIT" }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.62.0.tgz", - "integrity": "sha512-+g5O3j0w2ldzC86Pv6fvbO/xhAonbJFIdf/MKQ1d30gndlsVzUOE83ldfSE15Qrl9fhFjK6AovHs5Wpp6vx86w==", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.62.0", - "@typescript-eslint/typescript-estree": "8.62.0", - "@typescript-eslint/utils": "8.62.0", - "debug": "^4.4.3", - "ts-api-utils": "^2.5.0" - }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "node": ">=0.6" } }, - "node_modules/@typescript-eslint/types": { - "version": "8.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.62.0.tgz", - "integrity": "sha512-KvAclkktORPvM54TgLgA4z9HIV1M8zOgw9ZVNXl9f/8dLYfXYX1wkMXP7qmabpijQRV5bHJLOmoyGQbLMaUYeg==", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">= 14.0.0" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.62.0.tgz", - "integrity": "sha512-+hVbNxtW64pIcZWDPGbyaKF7vp2IBTVY5ma1blwwksrjdsbdqqEKvJWMGbBofei4F6Dovx1M0RJgoFeNu2279A==", - "dev": true, - "license": "MIT", + "node_modules/appium-xcuitest-driver/node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", + "license": "WTFPL", "dependencies": { - "@typescript-eslint/project-service": "8.62.0", - "@typescript-eslint/tsconfig-utils": "8.62.0", - "@typescript-eslint/types": "8.62.0", - "@typescript-eslint/visitor-keys": "8.62.0", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "utf8-byte-length": "^1.0.1" } }, - "node_modules/@typescript-eslint/utils": { - "version": "8.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.62.0.tgz", - "integrity": "sha512-82r66fi9zYwZ+mTq3vKgwjbZ1PVk/DJzrXFLpG6RnBbdvH8TEGVHIs9H4d2drhkOzf0syZuD/OZvvlu6GDbP4g==", - "dev": true, - "license": "MIT", + "node_modules/appium-xcuitest-driver/node_modules/type-fest": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.7.0.tgz", + "integrity": "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==", + "license": "(MIT OR CC0-1.0)", "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.62.0", - "@typescript-eslint/types": "8.62.0", - "@typescript-eslint/typescript-estree": "8.62.0" + "tagged-tag": "^1.0.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=20" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.62.0.tgz", - "integrity": "sha512-CY3uyFSRbcQv3nnSv8S0+lDftMVz6P963PoRlxrV7ew/Md564g9ut60PYzdLM5qW4jFn93GBF+Soi90ISAN+GQ==", - "dev": true, + }, + "node_modules/appium-xcuitest-driver/node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.62.0", - "eslint-visitor-keys": "^5.0.0" + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 18" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://opencollective.com/express" } }, - "node_modules/@vitest/expect": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", - "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", "license": "MIT", - "dependencies": { - "@standard-schema/spec": "^1.1.0", - "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", - "chai": "^6.2.2", - "tinyrainbow": "^3.1.0" + "engines": { + "node": ">=18" }, "funding": { - "url": "https://opencollective.com/vitest" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/@vitest/mocker": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", - "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "4.1.9", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.21" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + "node_modules/appium-xcuitest-driver/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "license": "Apache-2.0", + "optional": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } + "engines": { + "node": ">=14.17" } }, - "node_modules/@vitest/pretty-format": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", - "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/unorm": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz", + "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==", + "license": "MIT or GPL-2.0", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", - "dependencies": { - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">= 0.8" } }, - "node_modules/@vitest/runner": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", - "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/utf8-byte-length": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", + "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", + "license": "(WTFPL OR MIT)" + }, + "node_modules/appium-xcuitest-driver/node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/appium-xcuitest-driver/node_modules/uuid": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.0.tgz", + "integrity": "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "license": "Apache-2.0", "dependencies": { - "@vitest/utils": "4.1.9", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" } }, - "node_modules/@vitest/snapshot": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", - "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.9", - "@vitest/utils": "4.1.9", - "magic-string": "^0.30.21", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/@vitest/spy": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", - "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">= 0.8" } }, - "node_modules/@vitest/utils": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", - "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", "license": "MIT", + "optional": true, "dependencies": { - "@vitest/pretty-format": "4.1.9", - "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.1.0" + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" }, - "funding": { - "url": "https://opencollective.com/vitest" + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "node_modules/appium-xcuitest-driver/node_modules/winston": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz", + "integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==", "license": "MIT", "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.8", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" }, "engines": { - "node": ">= 0.6" + "node": ">= 12.0.0" } }, - "node_modules/accepts/node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "node_modules/appium-xcuitest-driver/node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", "license": "MIT", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, "engines": { - "node": ">= 0.6" + "node": ">= 12.0.0" } }, - "node_modules/accepts/node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "node_modules/appium-xcuitest-driver/node_modules/winston-transport/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", "dependencies": { - "mime-db": "^1.54.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">=18" + "node": ">= 6" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/winston/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/acorn": { - "version": "8.16.0", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/winston/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">=0.4.0" + "node": ">= 6" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/appium-xcuitest-driver/node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "node_modules/acorn-walk": { - "version": "8.3.5", - "dev": true, + "node_modules/appium-xcuitest-driver/node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/appium-xcuitest-driver/node_modules/yauzl": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.4.0.tgz", + "integrity": "sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==", "license": "MIT", "dependencies": { - "acorn": "^8.11.0" + "pend": "~1.2.0" }, "engines": { - "node": ">=0.4.0" + "node": ">=12" } }, - "node_modules/ajv": { - "version": "8.18.0", + "node_modules/appium-xcuitest-driver/node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" + "engines": { + "node": ">=12.20" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ajv-formats": { - "version": "3.0.1", + "node_modules/appium-xcuitest-driver/node_modules/zip-stream": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-7.0.5.tgz", + "integrity": "sha512-dSvYKdvLsAHCDqPOhIwk/q5CvuWtTB3Dgpoe0uVEFjTzIOAmsQpprX25InCvrvJsirEbu1OHyy67n/kAj1Sw/w==", "license": "MIT", "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" + "compress-commons": "^7.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } + "engines": { + "node": ">=18" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", + "node_modules/appium/node_modules/semver": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", + "node_modules/appium/node_modules/type-fest": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.7.0.tgz", + "integrity": "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==", + "license": "(MIT OR CC0-1.0)", "dependencies": { - "color-convert": "^2.0.1" + "tagged-tag": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">=20" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/anynum": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz", - "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==", + "node_modules/archiver": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-8.0.0.tgz", + "integrity": "sha512-fV1orZfsnPn9BaSByR/qE67rJCLJEy2Ox5bq7nJh+jquWaNh6Sfec75kJ2T6PtdGUbPQlrVoSVCEOa5SdiTQ1g==", + "license": "MIT", + "dependencies": { + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "is-stream": "^4.0.0", + "lazystream": "^1.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^3.0.0", + "tar-stream": "^3.0.0", + "zip-stream": "^7.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/archiver/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "funding": [ { "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" } ], - "license": "MIT" + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/archiver/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/archiver/node_modules/tar-stream": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.0.tgz", + "integrity": "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } }, "node_modules/arg": { "version": "4.1.3", "dev": true, "license": "MIT" }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, "node_modules/asap": { "version": "2.0.6", "dev": true, @@ -2124,6 +6897,18 @@ "node": ">=12" } }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/async-lock": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", + "license": "MIT" + }, "node_modules/async-mutex": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", @@ -2133,21 +6918,165 @@ "tslib": "^2.4.0" } }, + "node_modules/asyncbox": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/asyncbox/-/asyncbox-6.3.0.tgz", + "integrity": "sha512-7IFpnQDltd5rYQjhIJIpyismJtdWmw/pOABZKJfv2WVo0a6iYh2ZzUuCJJclae5mBtK0H/EychxXg91GB7rGdQ==", + "license": "Apache-2.0", + "dependencies": { + "p-limit": "^7.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + } + }, + "node_modules/asyncbox/node_modules/p-limit": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.0.tgz", + "integrity": "sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.2.1" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/asyncbox/node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/asynckit": { "version": "0.4.0", - "dev": true, "license": "MIT" }, + "node_modules/axios": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.0.tgz", + "integrity": "sha512-E32NzpYKp++W7XRe52rHiXV2ehxmh3wbdgO7MHeFM+vqxLBYHzt0ElkiImtOBxtOmyp0yoC8C6uESVV84Y2/hw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/b4a": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", + "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, "node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, "license": "MIT", "engines": { "node": "18 || 20 || >=22" } }, + "node_modules/bare-events": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz", + "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==", + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/bare-fs": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.4.tgz", + "integrity": "sha512-y1kC+ffIx/tPLdTE693uNjHfzTfr+ravR5tvWlMXe25nELbkqV400S71qHDwbkAQ1FVEZobB1NFRzFbCCcyBCQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.1.tgz", + "integrity": "sha512-JprUlveX3QjApC1cTpsUOiscADftCGVWkzitbHsRqv84hzYwYHw2mbluddsq5TvI8mH/8Ov1f4BiMAdcB0oYnQ==", + "license": "Apache-2.0" + }, + "node_modules/bare-stream": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.3.tgz", + "integrity": "sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.8.1", + "streamx": "^2.25.0", + "teex": "^1.0.1" + }, + "peerDependencies": { + "bare-abort-controller": "*", + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + }, + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.5.tgz", + "integrity": "sha512-K+y9xF1tN+CdPu4qWwr0QiK1Al07eFPGYK5M2pDXcmHdMdgC/tT/bpmMe1hrmRHaidKLkXrC+cRNYf3XVDUhSQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-path": "^3.0.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -2168,6 +7097,33 @@ ], "license": "MIT" }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, "node_modules/bindings": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", @@ -2188,6 +7144,12 @@ "readable-stream": "^3.4.0" } }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "license": "MIT" + }, "node_modules/body-parser": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", @@ -2225,11 +7187,31 @@ "url": "https://opencollective.com/express" } }, + "node_modules/bplist-creator": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.1.tgz", + "integrity": "sha512-Ese7052fdWrxp/vqSJkydgx/1MdBnNOCV2XVfbmdGWD2H6EYza+Q4pyYSuVSnCUD22hfI/BFI4jHaC3NLXLlJQ==", + "license": "MIT", + "dependencies": { + "stream-buffers": "2.2.x" + } + }, + "node_modules/bplist-parser": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.2.tgz", + "integrity": "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==", + "license": "MIT", + "dependencies": { + "big-integer": "1.6.x" + }, + "engines": { + "node": ">= 5.10.0" + } + }, "node_modules/brace-expansion": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -2262,6 +7244,15 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/bunyan": { "version": "1.8.15", "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", @@ -2322,6 +7313,22 @@ "node": ">=18" } }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/chownr": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", @@ -2346,6 +7353,30 @@ "node": ">=8" } }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -2360,6 +7391,15 @@ "node": ">=12" } }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, "node_modules/cmake-js": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-8.0.0.tgz", @@ -2407,6 +7447,19 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/color": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", + "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", + "license": "MIT", + "dependencies": { + "color-convert": "^3.1.3", + "color-string": "^2.1.3" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2416,18 +7469,59 @@ "color-name": "~1.1.4" }, "engines": { - "node": ">=7.0.0" + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", + "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-string/node_modules/color-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", + "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=14.6" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" + "node_modules/color/node_modules/color-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } }, "node_modules/combined-stream": { "version": "1.0.8", - "dev": true, "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" @@ -2444,6 +7538,62 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/compress-commons": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-7.0.1.tgz", + "integrity": "sha512-g0S8KAD8qf4+V//pr3BfB1aBnARLXNz2Gx+jmHU0LEriUuoQUOPOulVquHKTJ8+EAIIO7fhseNDr9wK5Q9FKBQ==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^7.0.1", + "is-stream": "^4.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/compress-commons/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/compress-commons/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2451,6 +7601,15 @@ "license": "MIT", "optional": true }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, "node_modules/content-disposition": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", @@ -2499,6 +7658,12 @@ "dev": true, "license": "MIT" }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, "node_modules/cors": { "version": "2.8.6", "license": "MIT", @@ -2514,6 +7679,71 @@ "url": "https://opencollective.com/express" } }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-7.0.1.tgz", + "integrity": "sha512-IBWsY8xznyQrcHn8h4bC8/4ErNke5elzgG8GcqF4RFPw6aHkWWRc7Tgw6upjaTX/CT/yQgqYENkxYsTYN+hW2g==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/crc32-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/crc32-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/create-require": { "version": "1.1.1", "dev": true, @@ -2579,9 +7809,20 @@ "dev": true, "license": "MIT" }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">=0.4.0" @@ -2603,6 +7844,13 @@ "node": ">=8" } }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "license": "MIT", + "optional": true + }, "node_modules/dezalgo": { "version": "1.0.4", "dev": true, @@ -2658,6 +7906,12 @@ "node": ">= 0.4" } }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "license": "MIT" + }, "node_modules/ee-first": { "version": "1.1.1", "license": "MIT" @@ -2704,6 +7958,12 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "license": "MIT" + }, "node_modules/encodeurl": { "version": "2.0.0", "license": "MIT", @@ -2759,7 +8019,6 @@ }, "node_modules/es-set-tostringtag": { "version": "2.1.0", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -3035,6 +8294,33 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, "node_modules/eventsource": { "version": "3.0.7", "license": "MIT", @@ -3159,6 +8445,12 @@ "version": "3.1.3", "license": "MIT" }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "license": "MIT" + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -3257,6 +8549,15 @@ "fxparser": "src/cli/cli.js" } }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -3275,6 +8576,12 @@ } } }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -3353,11 +8660,36 @@ "dev": true, "license": "ISC" }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "license": "MIT" + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/form-data": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", - "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -3437,6 +8769,35 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/ftp-response-parser": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ftp-response-parser/-/ftp-response-parser-1.0.1.tgz", + "integrity": "sha512-++Ahlo2hs/IC7UVQzjcSAfeUpCwTTzs4uvG5XfGnsinIFkWUYF4xWwPd5qZuK8MJrmUIxFMuHcfqaosCDjvIWw==", + "dependencies": { + "readable-stream": "^1.0.31" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/ftp-response-parser/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/ftp-response-parser/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "license": "MIT" + }, "node_modules/function-bind": { "version": "1.1.2", "license": "MIT", @@ -3453,6 +8814,18 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "license": "MIT", @@ -3583,6 +8956,22 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "license": "MIT", + "optional": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "license": "MIT", @@ -3595,7 +8984,6 @@ }, "node_modules/has-tostringtag": { "version": "1.0.2", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -3628,6 +9016,78 @@ "node": ">=16.9.0" } }, + "node_modules/hosted-git-info": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.3.tgz", + "integrity": "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==", + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT", + "optional": true + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "optional": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT", + "optional": true + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "optional": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "license": "MIT", + "optional": true + }, "node_modules/http-errors": { "version": "2.0.1", "license": "MIT", @@ -3646,6 +9106,25 @@ "url": "https://opencollective.com/express" } }, + "node_modules/http-status-codes": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", + "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==", + "license": "MIT" + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/iconv-lite": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", @@ -3772,13 +9251,46 @@ "is-extglob": "^2.1.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-promise": { - "version": "4.0.0", - "license": "MIT" - }, "node_modules/is-unsafe": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-1.0.1.tgz", @@ -3791,6 +9303,12 @@ ], "license": "MIT" }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "license": "ISC" @@ -3802,6 +9320,32 @@ "url": "https://github.com/sponsors/panva" } }, + "node_modules/jsftp": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/jsftp/-/jsftp-2.1.3.tgz", + "integrity": "sha512-r79EVB8jaNAZbq8hvanL8e8JGu2ZNr2bXdHC4ZdQhRImpSPpnWwm5DYVzQ5QxJmtGtKhNNuvqGgbNaFl604fEQ==", + "license": "MIT", + "dependencies": { + "debug": "^3.1.0", + "ftp-response-parser": "^1.0.1", + "once": "^1.4.0", + "parse-listing": "^1.1.3", + "stream-combiner": "^0.2.2", + "unorm": "^1.4.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsftp/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -3809,6 +9353,12 @@ "dev": true, "license": "MIT" }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, "node_modules/json-schema-traverse": { "version": "1.0.0", "license": "MIT" @@ -3852,6 +9402,78 @@ "json-buffer": "3.0.1" } }, + "node_modules/klaw": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-4.1.0.tgz", + "integrity": "sha512-1zGZ9MF9H22UnkpVeuaGKOjfA2t6WrfdrJmGjy16ykcjnKQDmHVX+KI477rpbGevz/5FD4MC3xf1oxylBgcaQw==", + "license": "MIT", + "engines": { + "node": ">=14.14.0" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "license": "MIT" + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -4127,6 +9749,18 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4143,12 +9777,63 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lockfile": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.4.tgz", + "integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==", + "license": "ISC", + "dependencies": { + "signal-exit": "^3.0.2" + } + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/long": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", "license": "Apache-2.0" }, + "node_modules/lru-cache": { + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -4192,9 +9877,38 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/method-override": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", + "integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==", + "license": "MIT", + "dependencies": { + "debug": "3.1.0", + "methods": "~1.1.2", + "parseurl": "~1.3.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/method-override/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/method-override/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/methods": { "version": "1.1.2", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -4202,7 +9916,6 @@ }, "node_modules/mime-db": { "version": "1.52.0", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -4210,7 +9923,6 @@ }, "node_modules/mime-types": { "version": "2.1.35", - "dev": true, "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -4219,6 +9931,15 @@ "node": ">= 0.6" } }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/mimic-response": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", @@ -4231,11 +9952,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC", + "optional": true + }, "node_modules/minimatch": { "version": "10.2.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.5" @@ -4306,6 +10033,41 @@ "node": "*" } }, + "node_modules/morgan": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.11.0.tgz", + "integrity": "sha512-zSkVu3t18r39pw4ixfBKvfZi3y2UOqr7d4WYwcj3m8nXpEQK4rPO6GLzs/CExoRgmX3y9EjmmcXqv6jq0SK46g==", + "license": "MIT", + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.4.1", + "on-headers": "~1.1.0" + }, + "engines": { + "node": ">= 0.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/morgan/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/morgan/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -4437,6 +10199,29 @@ "license": "MIT", "optional": true }, + "node_modules/normalize-package-data": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-8.0.0.tgz", + "integrity": "sha512-RWk+PI433eESQ7ounYxIp67CYuVsS1uYSonX3kA6ps/3LWfjVQa/ptEg6Y3T6uAMq1mWpX9PQ+qx+QaHpsc7gQ==", + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^9.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "license": "MIT", @@ -4454,6 +10239,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "license": "MIT", + "optional": true + }, "node_modules/obug": { "version": "2.1.1", "dev": true, @@ -4473,6 +10265,15 @@ "node": ">= 0.8" } }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "license": "ISC", @@ -4480,6 +10281,30 @@ "wrappy": "1" } }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "license": "MIT", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -4498,6 +10323,29 @@ "node": ">= 0.8.0" } }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -4530,6 +10378,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-listing": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/parse-listing/-/parse-listing-1.1.3.tgz", + "integrity": "sha512-a1p1i+9Qyc8pJNwdrSvW1g5TPxRH0sywVi6OzVvYHRo6xwF9bDWBxtH0KkxeOOvhUE8vAMtiSfsYQFOuK901eA==", + "engines": { + "node": ">=0.6.21" + } + }, "node_modules/parseurl": { "version": "1.3.3", "license": "MIT", @@ -4579,6 +10435,32 @@ "node": ">=8" } }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -4586,6 +10468,12 @@ "dev": true, "license": "MIT" }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "license": "ISC" @@ -4610,6 +10498,28 @@ "node": ">=16.20.0" } }, + "node_modules/plist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-4.0.0.tgz", + "integrity": "sha512-4dOqNo0Y2NpfSf9q4+zr4bh7pzNWeckIam34Z0KYJhg8qtNNfh59VbD+Yna5SjwcxawVvLKx5w5FtuCijpEF4Q==", + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.9.10", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/pngjs": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", @@ -4721,6 +10631,21 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -4765,6 +10690,15 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/pump": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", @@ -4857,6 +10791,21 @@ "node": ">= 6" } }, + "node_modules/readdir-glob": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-3.0.0.tgz", + "integrity": "sha512-AhNB2KgKeVJr16nK9LLZbJNWnYoT23ZrumNKFDebHBdkC8KHSqWo871JAUhoWC/RtjEVdqNMFpM6qrwRbaUqpw==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^10.2.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/yqnn" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -4873,6 +10822,19 @@ "node": ">=0.10.0" } }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/rimraf": { "version": "2.4.5", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", @@ -4935,16 +10897,6 @@ "node": ">= 18" } }, - "node_modules/router/node_modules/path-to-regexp": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", - "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -4972,10 +10924,35 @@ "license": "MIT", "optional": true }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "license": "MIT" }, + "node_modules/sanitize-filename": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.4.tgz", + "integrity": "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg==", + "license": "WTFPL OR ISC", + "dependencies": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "license": "MIT", + "optional": true + }, "node_modules/semver": { "version": "7.8.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", @@ -5039,6 +11016,31 @@ "url": "https://opencollective.com/express" } }, + "node_modules/serve-favicon": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.1.tgz", + "integrity": "sha512-JndLBslCLA/ebr7rS3d+/EKkzTsTi1jI2T9l+vHfAaGJ7A7NhtDpSZ0lx81HCNWnnE0yHncG+SSnVf9IMxOwXQ==", + "license": "MIT", + "dependencies": { + "etag": "~1.8.1", + "fresh": "~0.5.2", + "ms": "~2.1.3", + "parseurl": "~1.3.2", + "safe-buffer": "~5.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-favicon/node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/serve-static": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", @@ -5062,6 +11064,51 @@ "version": "1.2.0", "license": "ISC" }, + "node_modules/sharp": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.1.tgz", + "integrity": "sha512-lW979AMi+ESidzMv/Lnv+F9bknzLyxLqFI05Sm433vOeRcltgxQmXpnfOOFIAlKtwXU/ksupm2srQoFCkR214g==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.4" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.1", + "@img/sharp-darwin-x64": "0.35.1", + "@img/sharp-freebsd-wasm32": "0.35.1", + "@img/sharp-libvips-darwin-arm64": "1.3.0", + "@img/sharp-libvips-darwin-x64": "1.3.0", + "@img/sharp-libvips-linux-arm": "1.3.0", + "@img/sharp-libvips-linux-arm64": "1.3.0", + "@img/sharp-libvips-linux-ppc64": "1.3.0", + "@img/sharp-libvips-linux-riscv64": "1.3.0", + "@img/sharp-libvips-linux-s390x": "1.3.0", + "@img/sharp-libvips-linux-x64": "1.3.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.0", + "@img/sharp-libvips-linuxmusl-x64": "1.3.0", + "@img/sharp-linux-arm": "0.35.1", + "@img/sharp-linux-arm64": "0.35.1", + "@img/sharp-linux-ppc64": "0.35.1", + "@img/sharp-linux-riscv64": "0.35.1", + "@img/sharp-linux-s390x": "0.35.1", + "@img/sharp-linux-x64": "0.35.1", + "@img/sharp-linuxmusl-arm64": "0.35.1", + "@img/sharp-linuxmusl-x64": "0.35.1", + "@img/sharp-webcontainers-wasm32": "0.35.1", + "@img/sharp-win32-arm64": "0.35.1", + "@img/sharp-win32-ia32": "0.35.1", + "@img/sharp-win32-x64": "0.35.1" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "license": "MIT", @@ -5079,6 +11126,18 @@ "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.4.tgz", + "integrity": "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/side-channel": { "version": "1.1.0", "license": "MIT", @@ -5148,6 +11207,12 @@ "dev": true, "license": "ISC" }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, "node_modules/simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -5218,6 +11283,79 @@ "node": ">=0.10.0" } }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "license": "CC0-1.0" + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/stackback": { "version": "0.0.2", "dev": true, @@ -5242,6 +11380,36 @@ "node": ">= 0.8" } }, + "node_modules/stream-buffers": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", + "integrity": "sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==", + "license": "Unlicense", + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/stream-combiner": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", + "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==", + "license": "MIT", + "dependencies": { + "duplexer": "~0.1.1", + "through": "~2.3.4" + } + }, + "node_modules/streamx": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz", + "integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==", + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -5357,6 +11525,30 @@ "node": ">=14.18.0" } }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tagged-tag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", + "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tar": { "version": "7.5.19", "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.19.tgz", @@ -5407,6 +11599,49 @@ "node": ">=6" } }, + "node_modules/teen_process": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/teen_process/-/teen_process-4.1.3.tgz", + "integrity": "sha512-8W7Xp7WtJ5ZXjv0iHMsCgPPKzUt6ACfG/rDWX0tMIlMJaYcTYsPw3ZQQ9+hG7YsY+gm+DUATiyah3AraJ9JYpg==", + "license": "Apache-2.0", + "dependencies": { + "shell-quote": "^1.8.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": ">=10" + } + }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" + } + }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "license": "MIT" + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "license": "MIT" + }, "node_modules/tinybench": { "version": "2.9.0", "dev": true, @@ -5498,6 +11733,24 @@ } } }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", + "license": "WTFPL", + "dependencies": { + "utf8-byte-length": "^1.0.1" + } + }, "node_modules/ts-api-utils": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", @@ -5712,6 +11965,15 @@ "node": ">= 10.0.0" } }, + "node_modules/unorm": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz", + "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==", + "license": "MIT or GPL-2.0", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "license": "MIT", @@ -5735,17 +11997,46 @@ "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", "license": "MIT" }, + "node_modules/utf8-byte-length": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", + "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", + "license": "(WTFPL OR MIT)" + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/uuid": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.0.tgz", + "integrity": "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "dev": true, "license": "MIT" }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "node_modules/vary": { "version": "1.1.2", "license": "MIT", @@ -5931,6 +12222,25 @@ "dev": true, "license": "MIT" }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "license": "MIT", + "optional": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, "node_modules/which": { "version": "2.0.2", "license": "ISC", @@ -5959,6 +12269,54 @@ "node": ">=8" } }, + "node_modules/winston": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz", + "integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==", + "license": "MIT", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.8", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "license": "MIT", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -6026,6 +12384,15 @@ "node": ">=16.0.0" } }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -6086,6 +12453,18 @@ "node": ">=12" } }, + "node_modules/yauzl": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.4.0.tgz", + "integrity": "sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==", + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/yn": { "version": "3.1.1", "dev": true, @@ -6107,6 +12486,60 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zip-stream": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-7.0.5.tgz", + "integrity": "sha512-dSvYKdvLsAHCDqPOhIwk/q5CvuWtTB3Dgpoe0uVEFjTzIOAmsQpprX25InCvrvJsirEbu1OHyy67n/kAj1Sw/w==", + "license": "MIT", + "dependencies": { + "compress-commons": "^7.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/zip-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/zip-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/zod": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", @@ -6141,6 +12574,8 @@ "@fails-components/webtransport": "^1.6.3", "@fails-components/webtransport-transport-http3-quiche": "^1.6.3", "@modelcontextprotocol/sdk": "^1.20.0", + "appium": "^3.5.2", + "appium-xcuitest-driver": "^11.17.4", "tree-sitter": "^0.21.1", "tree-sitter-typescript": "^0.23.2" }, @@ -6337,6 +12772,8 @@ "@fails-components/webtransport-transport-http3-quiche": "^1.6.3", "@moq/lite": "^0.2.4", "@moq/net": "^0.1.1", + "appium": "^3.5.2", + "appium-xcuitest-driver": "^11.17.4", "bunyan": "^1.8.15", "bytes": "^3.1.2", "dotenv": "^17.4.2", diff --git a/packages/argent/package.json b/packages/argent/package.json index 913405c68..238897c89 100644 --- a/packages/argent/package.json +++ b/packages/argent/package.json @@ -1,7 +1,7 @@ { "name": "@swmansion/argent", "version": "0.15.0", - "description": "MCP server for iOS Simulator and Android Emulator control", + "description": "MCP server for iOS simulators, physical iPhones, Android devices, Chromium apps, and TV targets", "license": "Apache-2.0", "repository": { "type": "git", @@ -44,6 +44,8 @@ "@fails-components/webtransport": "^1.6.3", "@fails-components/webtransport-transport-http3-quiche": "^1.6.3", "@modelcontextprotocol/sdk": "^1.20.0", + "appium": "^3.5.2", + "appium-xcuitest-driver": "^11.17.4", "tree-sitter": "^0.21.1", "tree-sitter-typescript": "^0.23.2" }, diff --git a/packages/argent/scripts/bundle-tools.cjs b/packages/argent/scripts/bundle-tools.cjs index 1913bf7ab..4b3207f1a 100644 --- a/packages/argent/scripts/bundle-tools.cjs +++ b/packages/argent/scripts/bundle-tools.cjs @@ -104,6 +104,17 @@ const AX_TCP_BIN_SRC = path.resolve(BIN_SRC_ROOT, "darwin/tcp/ax-service"); const BIN_DIR = path.resolve(__dirname, "../bin"); const AX_BIN_DEST = path.resolve(BIN_DIR, "darwin/ax-service"); const AX_TCP_BIN_DEST = path.resolve(BIN_DIR, "darwin/tcp/ax-service"); +// argent-device-auth: macOS host helper for the branded physical-iOS tunnel +// auth prompt. Best-effort — only present once argent-private publishes the +// signed binary; until then physical iOS falls back to the osascript prompt. +const DEVICE_AUTH_BIN_SRC = path.resolve(BIN_SRC_ROOT, "darwin/argent-device-auth"); +const DEVICE_AUTH_BIN_DEST = path.resolve(BIN_DIR, "darwin/argent-device-auth"); +// Argent icon shown in that prompt (committed under native-devtools-ios/assets). +const DEVICE_ICON_SRC = path.resolve( + WORKSPACE_ROOT, + "packages/native-devtools-ios/assets/argent-icon.png" +); +const DEVICE_ICON_DEST = path.resolve(__dirname, "../assets/argent-icon.png"); // tvOS control binaries. Both are macOS-only: tvos-ax-service runs inside an // appletvsimulator, tvos-hid-daemon runs on the host. Unix-socket only. const TVOS_AX_BIN_SRC = path.resolve(BIN_SRC_ROOT, "darwin/tvos-ax-service"); @@ -208,6 +219,27 @@ const ASSETS = [ copiedLabel: "ax-service (tcp) binary", missLabel: "ax-service (tcp) binary", }, + // macOS host helper for the branded physical-iOS tunnel auth prompt. + // Best-effort: present once argent-private publishes the signed binary; + // until then physical iOS falls back to the (unbranded) osascript prompt. + { + kind: "file", + src: DEVICE_AUTH_BIN_SRC, + dest: DEVICE_AUTH_BIN_DEST, + mode: 0o755, + required: false, + copiedLabel: "argent-device-auth binary", + missLabel: "argent-device-auth binary", + }, + // Argent icon shown in the device-auth prompt (committed; best-effort copy). + { + kind: "file", + src: DEVICE_ICON_SRC, + dest: DEVICE_ICON_DEST, + required: false, + copiedLabel: "device-auth icon", + missLabel: "device-auth icon", + }, // tvOS AX reader — spawned inside an appletvsimulator via simctl to read // the focus-engine accessibility tree. macOS-only, unix-socket transport. { @@ -536,6 +568,16 @@ fs.mkdirSync(path.dirname(OUT_FILE), { recursive: true }); // are declared in @swmansion/argent's dependencies so npm installs them // alongside the package; keep them external so the bundle resolves them from // node_modules/ at runtime. +// +// `appium-xcuitest-driver` owns WebDriverAgent's Xcode project and other runtime +// assets under its package directory. Inlining only its JavaScript would leave +// those assets behind and make physical-iOS setup fail after npm installation. +// Keep the driver external and ship it (plus its Appium peer) as regular +// @swmansion/argent dependencies. +// Appium also installs `sharp` as an optional transitive dependency. Argent's +// Chromium screenshot path loads it dynamically and already degrades when it +// is absent, so keep the native addon external rather than asking esbuild to +// inline platform-specific `.node` files. buildBundle({ entry: TOOLS_ENTRY, out: OUT_FILE, @@ -545,6 +587,8 @@ buildBundle({ "tree-sitter", "tree-sitter-typescript", "electron", + "appium-xcuitest-driver", + "sharp", "@fails-components/webtransport", "@fails-components/webtransport-transport-http3-quiche", ], diff --git a/packages/argent/test/package-manifest.test.ts b/packages/argent/test/package-manifest.test.ts index a805dfd23..df2d99eff 100644 --- a/packages/argent/test/package-manifest.test.ts +++ b/packages/argent/test/package-manifest.test.ts @@ -12,6 +12,22 @@ describe("package manifest", () => { expect(pkg.files).toContain("bin/"); expect(pkg.files).toContain("skills/"); }); + + it("ships WebDriverAgent with the external physical-iOS driver", () => { + const workspaceRoot = path.resolve(import.meta.dirname, "..", "..", ".."); + const pkg = JSON.parse( + fs.readFileSync(path.join(workspaceRoot, "packages/argent/package.json"), "utf8") + ) as { dependencies?: Record }; + const bundleTools = fs.readFileSync( + path.join(workspaceRoot, "packages/argent/scripts/bundle-tools.cjs"), + "utf8" + ); + + expect(pkg.dependencies?.["appium-xcuitest-driver"]).toBeTruthy(); + expect(pkg.dependencies?.appium).toBeTruthy(); + expect(bundleTools).toContain('"appium-xcuitest-driver"'); + expect(bundleTools).toContain('"sharp"'); + }); }); // Regression guard for the ax-service-missing-from-release bug (first shipped diff --git a/packages/configuration-core/src/flags.ts b/packages/configuration-core/src/flags.ts index b9e61fe3b..6b1114e9f 100644 --- a/packages/configuration-core/src/flags.ts +++ b/packages/configuration-core/src/flags.ts @@ -57,6 +57,11 @@ export const FLAG_REGISTRY: readonly FlagDefinition[] = [ name: "artifacts-list-endpoint", description: "Expose GET /artifacts for remote artifact inventory consumers.", }, + { + name: "physical-ios-devices", + description: + "Discover and control physical iOS devices through an automatically signed WebDriverAgent session. Supports full touch/multi-touch, keyboard, screenshots, accessibility, app lifecycle, device logs, and native Time Profiler capture without a root tunnel.", + }, { name: "tool-server-event-log", description: "Write structured tool-server lifecycle events to a JSONL file.", diff --git a/packages/native-devtools-ios/assets/argent-icon.png b/packages/native-devtools-ios/assets/argent-icon.png new file mode 100644 index 000000000..dc54076a3 Binary files /dev/null and b/packages/native-devtools-ios/assets/argent-icon.png differ diff --git a/packages/native-devtools-ios/src/index.ts b/packages/native-devtools-ios/src/index.ts index 4be5101b9..75e39e599 100644 --- a/packages/native-devtools-ios/src/index.ts +++ b/packages/native-devtools-ios/src/index.ts @@ -9,6 +9,9 @@ import * as fs from "node:fs"; const DYLIB_DIR = process.env.ARGENT_NATIVE_DEVTOOLS_DIR ?? path.join(__dirname, "..", "dylibs"); const BIN_DIR = process.env.ARGENT_SIMULATOR_SERVER_DIR ?? path.join(__dirname, "..", "bin"); const DYLIB_TCP_DIR = process.env.ARGENT_NATIVE_DEVTOOLS_TCP_DIR ?? path.join(DYLIB_DIR, "tcp"); +// Committed static assets (e.g. the Argent icon shown in the device-auth prompt). +const ASSETS_DIR = + process.env.ARGENT_NATIVE_DEVTOOLS_ASSETS_DIR ?? path.join(__dirname, "..", "assets"); const DYLIB_TVOS_DIR = path.join(DYLIB_DIR, "tvos"); // The *local* and *tvos* accessors are darwin-only: the dylibs they return get @@ -146,6 +149,27 @@ export function axServiceBinaryPathTcp(): string { return requireBinIn(platformTcpBinDir(), "ax-service"); } +// argent-device-auth is the macOS host helper that pops the branded macOS +// authorization modal (password / Touch ID) and runs a command as root — +// Argent uses it to start the physical-iOS CoreDevice tunnel without a manual +// `sudo`. Unlike the resolvers above it returns null (rather than throwing) +// when absent, so callers can fall back to a less-branded escalation path. +// Override the binary with ARGENT_DEVICE_AUTH_HELPER (absolute path). +export function deviceAuthHelperPath(): string | null { + if (process.platform !== "darwin") return null; + const override = process.env.ARGENT_DEVICE_AUTH_HELPER; + if (override) return fs.existsSync(override) ? override : null; + const p = path.join(platformBinDir(), "argent-device-auth"); + return fs.existsSync(p) ? p : null; +} + +// Path to the Argent icon shown in the device-auth prompt, or null if missing. +// Override with ARGENT_DEVICE_ICON (absolute path to a PNG/icns). +export function argentIconPath(): string | null { + const p = process.env.ARGENT_DEVICE_ICON ?? path.join(ASSETS_DIR, "argent-icon.png"); + return fs.existsSync(p) ? p : null; +} + // tvOS control binaries. tvos-ax-service is `simctl spawn`d into an // appletvsimulator to read the focus-engine AX state; tvos-hid-daemon runs on // the host and injects Siri-remote HID via SimulatorKit. Both are darwin-only. diff --git a/packages/registry/src/errors.ts b/packages/registry/src/errors.ts index 1ace1a0fe..8e73eee6a 100644 --- a/packages/registry/src/errors.ts +++ b/packages/registry/src/errors.ts @@ -26,11 +26,13 @@ export const FAILURE_COMMANDS = [ "emulator", "vega", "xcrun_simctl", + "xcrun_devicectl", "xctrace", "native_devtools", "android_devtools", "ax_service", "simulator_server", + "pymobiledevice3", "cdp", "electron", "npm", diff --git a/packages/registry/src/failure-codes.ts b/packages/registry/src/failure-codes.ts index 6212304c2..3bb4fa9d0 100644 --- a/packages/registry/src/failure-codes.ts +++ b/packages/registry/src/failure-codes.ts @@ -79,6 +79,7 @@ export const FAILURE_CODES = { SIMULATOR_SERVER_READY_TIMEOUT: "SIMULATOR_SERVER_READY_TIMEOUT", SIMULATOR_SERVER_PROCESS_ERROR: "SIMULATOR_SERVER_PROCESS_ERROR", SIMULATOR_SERVER_TERMINATED: "SIMULATOR_SERVER_TERMINATED", + SIMULATOR_SERVER_PHYSICAL_DEVICE_UNSUPPORTED: "SIMULATOR_SERVER_PHYSICAL_DEVICE_UNSUPPORTED", AX_QUERY_TIMEOUT: "AX_QUERY_TIMEOUT", AX_DAEMON_READY_TIMEOUT: "AX_DAEMON_READY_TIMEOUT", @@ -86,6 +87,7 @@ export const FAILURE_CODES = { AX_DAEMON_PROCESS_ERROR: "AX_DAEMON_PROCESS_ERROR", AX_FACTORY_OPTIONS_MISSING: "AX_FACTORY_OPTIONS_MISSING", AX_WRONG_PLATFORM: "AX_WRONG_PLATFORM", + AX_PHYSICAL_DEVICE_UNSUPPORTED: "AX_PHYSICAL_DEVICE_UNSUPPORTED", AX_DEVICE_ID_INVALID: "AX_DEVICE_ID_INVALID", AX_DESCRIBE_ERROR: "AX_DESCRIBE_ERROR", AX_QUERY_FAILED: "AX_QUERY_FAILED", @@ -96,9 +98,39 @@ export const FAILURE_CODES = { ANDROID_REINSTALL_INSTALL_FAILED: "ANDROID_REINSTALL_INSTALL_FAILED", ANDROID_RESTART_FAILED: "ANDROID_RESTART_FAILED", IOS_LAUNCH_SIMCTL_FAILED: "IOS_LAUNCH_SIMCTL_FAILED", + IOS_LAUNCH_DEVICECTL_FAILED: "IOS_LAUNCH_DEVICECTL_FAILED", IOS_OPEN_URL_FAILED: "IOS_OPEN_URL_FAILED", IOS_REINSTALL_INSTALL_FAILED: "IOS_REINSTALL_INSTALL_FAILED", IOS_RESTART_LAUNCH_FAILED: "IOS_RESTART_LAUNCH_FAILED", + + CORE_DEVICE_FLAG_DISABLED: "CORE_DEVICE_FLAG_DISABLED", + CORE_DEVICE_FACTORY_OPTIONS_MISSING: "CORE_DEVICE_FACTORY_OPTIONS_MISSING", + CORE_DEVICE_WRONG_DEVICE: "CORE_DEVICE_WRONG_DEVICE", + CORE_DEVICE_PMD3_NOT_FOUND: "CORE_DEVICE_PMD3_NOT_FOUND", + CORE_DEVICE_TUNNEL_UNREACHABLE: "CORE_DEVICE_TUNNEL_UNREACHABLE", + CORE_DEVICE_TUNNEL_NOT_REGISTERED: "CORE_DEVICE_TUNNEL_NOT_REGISTERED", + CORE_DEVICE_TUNNEL_AUTH_DECLINED: "CORE_DEVICE_TUNNEL_AUTH_DECLINED", + CORE_DEVICE_TUNNEL_TIMEOUT: "CORE_DEVICE_TUNNEL_TIMEOUT", + CORE_DEVICE_IOS_VERSION_TOO_OLD: "CORE_DEVICE_IOS_VERSION_TOO_OLD", + CORE_DEVICE_COMMAND_FAILED: "CORE_DEVICE_COMMAND_FAILED", + + PHYSICAL_IOS_AUTOMATION_FACTORY_OPTIONS_MISSING: + "PHYSICAL_IOS_AUTOMATION_FACTORY_OPTIONS_MISSING", + PHYSICAL_IOS_AUTOMATION_WRONG_DEVICE: "PHYSICAL_IOS_AUTOMATION_WRONG_DEVICE", + PHYSICAL_IOS_AUTOMATION_DEVICE_DETAILS_FAILED: + "PHYSICAL_IOS_AUTOMATION_DEVICE_DETAILS_FAILED", + PHYSICAL_IOS_AUTOMATION_SIGNING_TEAM_NOT_FOUND: + "PHYSICAL_IOS_AUTOMATION_SIGNING_TEAM_NOT_FOUND", + PHYSICAL_IOS_AUTOMATION_WDA_BUILD_FAILED: "PHYSICAL_IOS_AUTOMATION_WDA_BUILD_FAILED", + PHYSICAL_IOS_AUTOMATION_WDA_SESSION_FAILED: + "PHYSICAL_IOS_AUTOMATION_WDA_SESSION_FAILED", + PHYSICAL_IOS_AUTOMATION_COMMAND_FAILED: "PHYSICAL_IOS_AUTOMATION_COMMAND_FAILED", + + DEVICE_LOG_SESSION_FACTORY_OPTIONS_MISSING: "DEVICE_LOG_SESSION_FACTORY_OPTIONS_MISSING", + DEVICE_LOG_SESSION_ALREADY_ACTIVE: "DEVICE_LOG_SESSION_ALREADY_ACTIVE", + DEVICE_LOG_SESSION_NOT_ACTIVE: "DEVICE_LOG_SESSION_NOT_ACTIVE", + DEVICE_LOG_SESSION_START_FAILED: "DEVICE_LOG_SESSION_START_FAILED", + NATIVE_DEVTOOLS_DESCRIBE_ERROR: "NATIVE_DEVTOOLS_DESCRIBE_ERROR", NATIVE_DEVTOOLS_VIEW_AT_POINT_ERROR: "NATIVE_DEVTOOLS_VIEW_AT_POINT_ERROR", NATIVE_DEVTOOLS_USER_INTERACTABLE_VIEW_AT_POINT_ERROR: diff --git a/packages/skills/package.json b/packages/skills/package.json index d50b9addd..8e4c36e5a 100644 --- a/packages/skills/package.json +++ b/packages/skills/package.json @@ -3,7 +3,7 @@ "name": "@argent/skills", "version": "0.15.0", "type": "module", - "description": "Claude Code skills for iOS simulator and Android emulator interaction via argent", + "description": "Agent skills for iOS simulators, physical iPhones, Android devices, Chromium apps, and TV targets via Argent", "scripts": { "install-skills": "node scripts/install.js" }, diff --git a/packages/skills/rules/argent.md b/packages/skills/rules/argent.md index d4960a3d3..d8e513378 100644 --- a/packages/skills/rules/argent.md +++ b/packages/skills/rules/argent.md @@ -1,17 +1,17 @@ --- -description: Argent iOS Simulator and Android Emulator Agent — always-on guidance for methodology and tools for working with, interacting, testing and profiling mobile app work +description: Argent mobile-device agent — always-on guidance for controlling, testing, debugging, and profiling iOS simulators, physical iPhones, Android devices, Chromium apps, and TV targets alwaysApply: true --- -If argent is installed and configured in this environment, its MCP tools are the preferred form of interaction with the application for iOS simulator, Android emulator, Chromium (CDP) app, and Vega (Amazon Fire TV) device control; otherwise see `` below before attempting any argent workflow. A "Chromium (CDP) app" is any Chromium runtime exposing a Chrome DevTools Protocol endpoint — an Electron app, or any Chromium-family browser (Chrome/Brave/Edge) launched with `--remote-debugging-port`; all are driven through the same tool surface and tagged `platform: "chromium"`. A "Vega device" is a virtual device (VVD) or physical unit — driven by tv-remote (D-pad) and tagged `platform: "vega"`. +If argent is installed and configured in this environment, its MCP tools are the preferred form of interaction with the application for iOS simulators, connected physical iPhones, Android devices, Chromium (CDP) apps, and Vega (Amazon Fire TV) targets; otherwise see `` below before attempting any argent workflow. A physical iPhone appears in `list-devices` with `platform: "ios"`, `kind: "device"`, and a CoreDevice UDID such as `00008130-…`; enable it once with `argent enable physical-ios-devices`. A "Chromium (CDP) app" is any Chromium runtime exposing a Chrome DevTools Protocol endpoint — an Electron app, or any Chromium-family browser (Chrome/Brave/Edge) launched with `--remote-debugging-port`; all are driven through the same tool surface and tagged `platform: "chromium"`. A "Vega device" is a virtual device (VVD) or physical unit — driven by tv-remote (D-pad) and tagged `platform: "vega"`. Running MCP server and managing the Argent toolkit utilises `argent` command - if asked use `argent --help` for reference. To check current version of MCP server run `argent --version` command. Use cases: -- User mentions iOS simulator, Android emulator, device, or app interaction -- The app user is working with is a mobile application which can be run in a simulator/emulator +- User mentions an iOS simulator, physical iPhone, Android device, or app interaction +- The app user is working with is a mobile application which can run in a simulator/emulator or on a connected phone - Any tapping, swiping, typing, screenshotting, or inspecting a running app - Any code change that affects visible mobile UI, layout, styling, copy, navigation, or screen composition - Any request to execute manual QA, UI QA, or visual behavior validation for a mobile app @@ -60,7 +60,7 @@ Before booting, running, or interacting with any app, call `list-devices` first Decision order: 1. **Explicit user intent** - choose the user named platform or device. Look for words "simulator" and "emulator". -2. **Prefer a running device.** iOS simulators - state `Booted` and Android devices - `state: "device"` come first in `list-devices`; Chromium (CDP) apps appear as `platform: "chromium"`, `state: "Running"`. +2. **Prefer a running device.** iOS simulators have state `Booted`, physical iPhones have `kind: "device"` and state `connected`, and Android devices have state `device`; Chromium (CDP) apps appear as `platform: "chromium"`, `state: "Running"`. 3. **Single-platform project:** (per `argent-environment-inspector` flags `is_native_ios`/`is_native_android`, or RN with only one platform configured) → boot that platform. @@ -70,7 +70,7 @@ Decision order: -- All simulator/emulator interactions go through argent MCP tools — never use `xcrun simctl`, +- All simulator/emulator/physical-iPhone interactions go through argent MCP tools — never use `xcrun simctl`, raw `curl` to simulator ports, or the simulator-server binary directly. - Before calling any gesture tool for the first time, use ToolSearch to load its schema. - Interaction tools (`gesture-tap`, `gesture-swipe`, `gesture-pinch`, `gesture-rotate`, `gesture-custom`, `launch-app`, etc.) return a screenshot automatically. @@ -110,7 +110,7 @@ When: Beginning a task that involves the Android emulator, no emulator running y TAPPING, SWIPING, TYPING, GESTURES, SCREENSHOTS, SCROLLING Skill: `argent-device-interact` -When: Performing touch interactions, typing, pressing hardware buttons, launching/restarting apps, opening URLs, rotating device, taking standalone screenshots, or verifying a visible UI code change. Phone/tablet iOS and Android only — for any TV target use the TV skill below. +When: Performing touch interactions, typing, pressing hardware buttons, launching/restarting apps, opening URLs, rotating a device, taking standalone screenshots, or verifying a visible UI change. Covers iOS simulators, connected physical iPhones, and Android phones/tablets — for any TV target use the TV skill below. TV INTERACTION (APPLE TV / ANDROID TV / FIRE TV) Skill: `argent-tv-interact` @@ -135,7 +135,7 @@ When: To measure performance of specific components, to find app-wide bottleneck NATIVE PROFILING Use skill: `argent-native-profiler` -When: Profiling native performance (CPU hotspots, UI hangs, memory leaks). iOS only today; Android on the roadmap. Useful as a reference for platform-specific investigation when running dual profiling via `argent-react-native-profiler`. +When: Profiling native performance (CPU hotspots, UI hangs, memory leaks). Supports iOS simulators and physical iPhones; protected system apps on a phone provide device-wide Time Profiler data filtered to the app PID rather than Leaks/Allocations. Android is also supported through Perfetto. Useful as a reference for platform-specific investigation when running dual profiling via `argent-react-native-profiler`. PERFORMANCE OPTIMIZATION Use skill: `argent-react-native-optimization` diff --git a/packages/skills/skills/argent-device-interact/SKILL.md b/packages/skills/skills/argent-device-interact/SKILL.md index 0758b17c6..35b5cc176 100644 --- a/packages/skills/skills/argent-device-interact/SKILL.md +++ b/packages/skills/skills/argent-device-interact/SKILL.md @@ -1,11 +1,11 @@ --- name: argent-device-interact -description: Interact with an iOS simulator, Android emulator, or Chromium (CDP) app using argent MCP tools. Use when tapping UI elements, performing gestures, scrolling/swiping, typing text, pressing hardware buttons, launching apps, opening URLs, taking screenshots, waiting for an element to appear or disappear, or checking visible app state after interactions. +description: Interact with an iOS simulator, connected physical iPhone, Android device, or Chromium (CDP) app using Argent MCP tools. Use when tapping, swiping, pinching, rotating, typing, pressing hardware buttons, launching apps, opening URLs, taking screenshots, waiting for UI state, or validating visible app behavior. --- ## Unified tool surface -All interaction tools below accept a `udid` parameter and auto-dispatch iOS vs Android based on its shape (UUID → iOS simulator, `chromium-cdp-` → Chromium (CDP) app, anything else → Android adb serial). You use the same tool names on every platform. +All interaction tools below accept a `udid` parameter and auto-dispatch from `list-devices`: UUID → iOS simulator, CoreDevice UDID such as `00008130-…` → physical iPhone, `chromium-cdp-` → Chromium app, and Android serial → Android. Use the same tool names on every platform. **Chromium (CDP) app** = any Chromium runtime exposing a Chrome DevTools Protocol endpoint: an Electron app (boot it with `boot-device` + `electronAppPath`), or any Chromium-family browser (Chrome/Brave/Edge) launched with `--remote-debugging-port`. The latter is auto-discovered by `list-devices` on port `9222` plus anything in `ARGENT_CHROMIUM_PORTS`. The same describe/tap/swipe/keyboard/screenshot surface drives all of them. @@ -19,10 +19,12 @@ For platform-specific caveats (Metro `adb reverse`, locked-screen describe error ## 1. Before You Start -If you delegate simulator tasks to sub-agents, make sure they have MCP permissions. +If you delegate device tasks to sub-agents, make sure they have MCP permissions. Use `list-devices` to get a target id. Results are tagged with `platform` (`ios`, `android`, or `chromium`); booted/ready devices come first. Pick the first entry that matches the platform you need — if none are ready, call `boot-device` with `udid` (iOS), `avdName` (Android), or `electronAppPath` (boots an Electron app as a `chromium` device). A Chromium browser already running with a CDP port shows up directly — no `boot-device` needed. See `argent-ios-simulator-setup` / `argent-android-emulator-setup` for full setup flow. +For a physical iPhone, first run `argent enable physical-ios-devices`. Keep the connected phone unlocked and trusted with Developer Mode enabled. `boot-device` does not reboot the phone; it prepares the signed WebDriverAgent session. The first call may take several seconds, while later controls reuse it. + **Load tool schemas before first use.** Gesture tools (`gesture-tap`, `gesture-swipe`, `gesture-pinch`, `gesture-rotate`, `gesture-custom`) may be deferred — their parameter schemas are not loaded until fetched. Always use ToolSearch to load the schemas of all gesture tools you plan to use **before** calling any of them. If you skip this step, parameters may be coerced to strings instead of numbers, causing validation errors. ## 2. Best Practices @@ -32,7 +34,7 @@ Use `list-devices` to get a target id. Results are tagged with `platform` (`ios` 3. **Use `gesture-swipe` for lists/scrolling**, not `gesture-custom`, unless you need non-linear movement. On Chromium use `gesture-scroll` instead — `gesture-swipe` is touch-only. Consider whether you need multiple swipes, if yes - use `run-sequence`. 4. **Tap a text field before typing**, then use `keyboard` to enter text. 5. **Coordinates are normalized** — always 0.0–1.0, not pixels. -6. **For app navigation, prefer `describe` first.** It works on any screen without app restart. Do not navigate from screenshots on regular in-app screens unless `describe` failed to expose a reliable target. Use `native-describe-screen` only when you need app-scoped UIKit properties. +6. **For app navigation, prefer `describe` first.** It works on any screen without app restart. Do not navigate from screenshots on regular in-app screens unless `describe` failed to expose a reliable target. `native-describe-screen` is simulator-only; physical iPhones use the exact nested XCTest hierarchy returned by `describe`. ## 3. Opening Apps @@ -339,4 +341,7 @@ Stops on the first error (or unmet `await-ui-element` condition) and returns par ### iOS -_(no iOS-only gotchas collected here yet — add them as they come up)_ +- A `kind: "device"` entry is a physical iPhone. Enable it with `argent enable physical-ios-devices`, then keep it connected, unlocked, trusted, and in Developer Mode. +- Physical controls, screenshots, keyboard input, accessibility, app lifecycle, unified logs, and native profiling use the same tools as a simulator. +- XCTest does not expose App Switcher on a physical phone. Home, Power, volume, and Action buttons are supported; Power may require manual passcode unlock afterward. +- Injected `native-*` UIKit/React inspection is simulator-only. On a physical phone use `describe`, screenshots, `device-logs-*`, debugger tools for debuggable apps, and `native-profiler-*`. diff --git a/packages/tool-server/package.json b/packages/tool-server/package.json index db8c30037..a7a30da63 100644 --- a/packages/tool-server/package.json +++ b/packages/tool-server/package.json @@ -2,7 +2,7 @@ "private": true, "name": "@argent/tool-server", "version": "0.15.0", - "description": "Framework-agnostic tool registry for iOS simulator and Android emulator control", + "description": "Framework-agnostic tool registry for iOS/Android simulator, emulator, and physical-device control", "main": "dist/index.js", "scripts": { "build": "rm -rf dist tsconfig.tsbuildinfo && tsc && cp src/utils/ios-profiler/Argent.tracetemplate dist/utils/ios-profiler/", @@ -26,6 +26,8 @@ "@fails-components/webtransport-transport-http3-quiche": "^1.6.3", "@moq/lite": "^0.2.4", "@moq/net": "^0.1.1", + "appium": "^3.5.2", + "appium-xcuitest-driver": "^11.17.4", "bunyan": "^1.8.15", "bytes": "^3.1.2", "dotenv": "^17.4.2", diff --git a/packages/tool-server/src/blueprints/ax-service.ts b/packages/tool-server/src/blueprints/ax-service.ts index 7e7a48116..14a652e3e 100644 --- a/packages/tool-server/src/blueprints/ax-service.ts +++ b/packages/tool-server/src/blueprints/ax-service.ts @@ -235,6 +235,21 @@ export const axServiceBlueprint: ServiceBlueprint = { } ); } + if (device.kind === "device") { + // ax-service uses `xcrun simctl spawn`, which only works on simulators. + // Physical iPhones expose their live XCTest tree through WebDriverAgent. + // `describe` routes there directly; this is a backstop for a wrong + // ax-service resolution. + throw new FailureError( + `${AX_SERVICE_NAMESPACE} is iOS-simulator-only. The physical device '${device.id}' is driven over WebDriverAgent; use describe for its live XCTest tree instead.`, + { + error_code: FAILURE_CODES.AX_PHYSICAL_DEVICE_UNSUPPORTED, + failure_stage: "ax_service_factory_platform", + failure_area: "tool_server", + error_kind: "unsupported", + } + ); + } // Reject before spawning. An undefined `device.id` slips through when an // inner tool is invoked via a wrapper that doesn't re-validate the inner // schema. Without this guard `getSocketPath(undefined).slice` would crash diff --git a/packages/tool-server/src/blueprints/core-device.ts b/packages/tool-server/src/blueprints/core-device.ts new file mode 100644 index 000000000..882b52e03 --- /dev/null +++ b/packages/tool-server/src/blueprints/core-device.ts @@ -0,0 +1,619 @@ +import { execFile } from "node:child_process"; +import { promisify } from "node:util"; +import { existsSync } from "node:fs"; +import { writeFile } from "node:fs/promises"; +import { tmpdir, homedir } from "node:os"; +import { join } from "node:path"; +import { randomUUID } from "node:crypto"; +import { + TypedEventEmitter, + FAILURE_CODES, + FailureError, + type DeviceInfo, + type ServiceBlueprint, + type ServiceInstance, + type ServiceEvents, +} from "@argent/registry"; +import { isFlagEnabled } from "@argent/configuration-core"; +import { deviceAuthHelperPath, argentIconPath } from "@argent/native-devtools-ios"; +import { + CoreDeviceAgent, + CoreDeviceAgentError, + materializeAgentScript, + resolvePmd3Python, +} from "./coredevice-agent"; + +const execFileAsync = promisify(execFile); + +const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); + +export const CORE_DEVICE_NAMESPACE = "CoreDevice"; + +// Opt-in flag (also gates discovery in list-devices). The privileged tunnel +// start must not be reachable unless the user enabled the experimental feature. +const PHYSICAL_IOS_FLAG = "physical-ios-devices"; + +/** + * Throw the standard "enable the flag" error unless physical-iOS support is on. + * The single gate for every physical-iOS operation — the CoreDevice factory and + * tunnel start funnel through it, and `launch-app` (which drives a real device + * via `devicectl` rather than the CoreDevice backend) calls it directly so it + * can't bypass the opt-in. + */ +export function assertPhysicalIosEnabled(): void { + if (!isFlagEnabled(PHYSICAL_IOS_FLAG)) { + throw new FailureError( + `Physical iOS support is disabled. Enable it with: argent enable ${PHYSICAL_IOS_FLAG}`, + { + error_code: FAILURE_CODES.CORE_DEVICE_FLAG_DISABLED, + failure_stage: "core_device_flag_gate", + failure_area: "tool_server", + error_kind: "unsupported", + } + ); + } +} + +// The registry's `ServiceRef.options` is typed as `Record`; +// the intersection adds the implicit string index signature an interface lacks. +type CoreDeviceFactoryOptions = Record & { device: DeviceInfo }; + +/** + * Backend for a physical iOS device, driven over Apple's CoreDevice "remote + * control" services via the `pymobiledevice3` CLI — no app installed on the + * device. This is a separate blueprint from the simulator-server because real + * iPhones speak an entirely different transport (the iOS-17+ RemoteXPC tunnel), + * mirroring how physical Android uses its own `android_device` controller. + * + * Requirements (all surfaced as actionable errors): iOS 27+ (Apple gates the + * touch/"remote control" services to 27.0+), `pymobiledevice3` installed, and a + * running CoreDevice tunnel (`sudo pymobiledevice3 remote tunneld`, which needs + * root to create the tunnel interface — every command here then runs unprivileged). + */ +export interface CoreDeviceAxElement { + /** VoiceOver caption: label + value + traits, e.g. "Wi-Fi, 1, Button, Toggle". */ + caption: string; + /** Per-element identifier (hex) — stable within a single snapshot. */ + id: string; + /** On-screen rect "{{x, y}, {w, h}}" in points, when the audit reported one. */ + rect?: string; +} + +export interface CoreDeviceAxTree { + elements: CoreDeviceAxElement[]; + /** Screen size in points, for normalizing rects to 0..1. */ + screen?: { w?: number; h?: number }; +} + +export interface CoreDeviceApi { + /** Capture a PNG to a temp file and return its path. */ + screenshot(): Promise<{ path: string }>; + /** Tap at normalized (x, y) in 0..1. */ + tap(x: number, y: number): Promise; + /** Swipe/drag from (fromX, fromY) to (toX, toY), all normalized 0..1. */ + swipe(fromX: number, fromY: number, toX: number, toY: number, durationMs: number): Promise; + /** Press a hardware button by its pymobiledevice3 name (home/lock/volume-up/volume-down/...). */ + button(name: string): Promise; + /** + * The on-screen accessibility tree of the frontmost app (or the home screen), + * via the iOS-26+ axAudit service — rich semantic captions + reading order for + * every element, plus on-screen rects for the subset the accessibility audit + * flags (Apple doesn't expose per-element geometry on hardware). Backs `describe`. + */ + axtree(): Promise; +} + +export function coreDeviceRef(device: DeviceInfo): { + urn: string; + options: CoreDeviceFactoryOptions; +} { + return { + urn: `${CORE_DEVICE_NAMESPACE}:${device.id}`, + options: { device }, + }; +} + +interface Rsd { + address: string; + port: number; +} + +function tunneldPort(): number { + const raw = process.env.ARGENT_PMD3_TUNNELD_PORT; + const n = raw ? Number.parseInt(raw, 10) : NaN; + return Number.isInteger(n) && n > 0 && n <= 65535 ? n : 49151; +} + +/** Resolve the pymobiledevice3 executable: env override, common install dirs, then PATH. */ +function resolvePmd3(): string { + const override = process.env.ARGENT_PYMOBILEDEVICE3; + if (override) return override; + const candidates = [ + join(homedir(), ".local", "bin", "pymobiledevice3"), + "/opt/homebrew/bin/pymobiledevice3", + "/usr/local/bin/pymobiledevice3", + ]; + for (const c of candidates) if (existsSync(c)) return c; + return "pymobiledevice3"; +} + +/** + * Fail fast with an install hint when pymobiledevice3 is missing, rather than + * surfacing a raw `spawn ENOENT` from the first interaction. Runs `version` (a + * cheap subcommand); a missing binary throws ENOENT, anything else is tolerated. + */ +async function verifyPmd3Available(pmd3: string): Promise { + try { + await execFileAsync(pmd3, ["version"], { timeout: 10_000 }); + } catch (err) { + if ((err as { code?: string })?.code === "ENOENT") { + throw new FailureError( + `pymobiledevice3 was not found (tried "${pmd3}"). Physical iOS control needs it — ` + + `install it (e.g. \`pipx install pymobiledevice3\`) or set ARGENT_PYMOBILEDEVICE3 to its path.`, + { + error_code: FAILURE_CODES.CORE_DEVICE_PMD3_NOT_FOUND, + failure_stage: "core_device_verify_pmd3", + failure_area: "tool_server", + error_kind: "dependency_missing", + failure_command: "pymobiledevice3", + }, + { cause: err instanceof Error ? err : new Error(String(err)) } + ); + } + // Non-ENOENT (odd `version` failure): don't block; a real command will report. + } +} + +/** Resolve pymobiledevice3 to an absolute path — the privileged root shell does + * not inherit the user's PATH (so `~/.local/bin` is invisible to it). */ +async function resolvePmd3Absolute(): Promise { + const p = resolvePmd3(); + if (p.startsWith("/")) return p; + try { + const { stdout } = await execFileAsync("/usr/bin/which", [p], { timeout: 5_000 }); + const abs = stdout.trim(); + if (abs.startsWith("/")) return abs; + } catch { + // fall through to the install hint + } + throw new FailureError( + `pymobiledevice3 was not found on PATH. Install it (e.g. \`pipx install pymobiledevice3\`) ` + + `or set ARGENT_PYMOBILEDEVICE3 to its absolute path.`, + { + error_code: FAILURE_CODES.CORE_DEVICE_PMD3_NOT_FOUND, + failure_stage: "core_device_resolve_pmd3_absolute", + failure_area: "tool_server", + error_kind: "dependency_missing", + failure_command: "pymobiledevice3", + } + ); +} + +/** Single-quote a string for safe use as one /bin/sh word. */ +function shellSingleQuote(s: string): string { + return `'${s.replace(/'/g, `'\\''`)}'`; +} + +/** Escape a /bin/sh command string for embedding inside an AppleScript double-quoted literal. */ +export function appleScriptQuote(cmd: string): string { + return cmd.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); +} + +/** + * The /bin/sh command (run as root) that starts a daemonized tunneld on `port`. + * HOME is pinned to root's home so pymobiledevice3 finds its RemoteXPC pairing + * records: the privileged-exec environment (Authorization Services) doesn't + * inherit a usable HOME, which otherwise leaves tunneld unable to form the + * device tunnel (it starts but never registers one). sudo set HOME=/var/root + * implicitly; here we set it explicitly. + * + * The log goes under /var/root (root's home, mode 0700) rather than a predictable + * path in world-writable /tmp: this command runs as root, and a root `>` redirect + * to a /tmp path a non-root user can pre-symlink is a classic privileged-tmp + * clobber (CWE-59). + */ +export function tunneldStartCommand(pmd3Abs: string, port: number): string { + return `HOME=/var/root ${shellSingleQuote(pmd3Abs)} remote tunneld --port ${port} -d > /var/root/argent-coredevice-tunneld.log 2>&1`; +} + +/** + * Start `pymobiledevice3 remote tunneld` as root via the standard macOS + * authorization dialog (password / Touch ID where the OS offers it) — so users + * never run sudo by hand. AppleScript's `with administrator privileges` routes + * through Authorization Services and shows the system modal in the active GUI + * session; `-d` daemonizes so the privileged shell returns immediately, leaving + * tunneld running as root for the rest of the session. Throws if the user + * cancels or no GUI session is available (headless) — callers fall back to the + * manual `sudo` instructions. + */ +async function startTunneldWithPrivilege(pmd3Abs: string, port: number): Promise { + const shellCmd = tunneldStartCommand(pmd3Abs, port); + // Generous timeout: the user has to see and approve the modal. + const timeout = 120_000; + + // Preferred: the signed host helper, which shows the modal branded as + // "Argent" with the Argent icon + a clear message via Authorization Services. + const helper = deviceAuthHelperPath(); + if (helper) { + const icon = argentIconPath() ?? ""; + const prompt = "Argent needs administrator access to connect to a physical iOS device."; + try { + await execFileAsync(helper, [icon, prompt, "/bin/sh", "-c", shellCmd], { timeout }); + return; + } catch (err) { + // Exit 3 = the user explicitly cancelled the prompt — respect that and + // don't pop a second (osascript) prompt. Any other failure (a broken, + // unsigned, quarantined, or 0-byte helper binary) degrades to the + // osascript admin prompt below rather than hard-failing. + if ((err as { code?: unknown }).code === 3) throw err; + } + } + + // Fallback when the helper isn't installed (e.g. a dev tree without the signed + // binary), is unusable, or there's no GUI: the generic osascript admin prompt. + // Functional but unbranded ("osascript wants to make changes"). + const appleScript = `do shell script "${appleScriptQuote(shellCmd)}" with administrator privileges`; + await execFileAsync("osascript", ["-e", appleScript], { timeout }); +} + +// Dedupe concurrent escalation prompts: parallel interactions share one modal. +// Cleared after settle so a later call can retry if the user cancelled. +let tunnelStartInFlight: Promise | null = null; + +function tunnelHelp(udid: string, reason: string): string { + const port = tunneldPort(); + // Echo the custom port in the manual command — otherwise a user who set + // ARGENT_PMD3_TUNNELD_PORT would start tunneld on pmd3's default (49151) and + // discovery, which probes the custom port, would never find it. + const portFlag = port === 49151 ? "" : ` --port ${port}`; + return ( + `Physical iOS control needs a CoreDevice tunnel for ${udid}, but ${reason} ` + + `(checked tunneld at 127.0.0.1:${port}). Argent tries to start it automatically via the macOS ` + + `authorization prompt; if that was declined or no GUI session is available, start it manually ` + + `and leave it running:\n sudo pymobiledevice3 remote tunneld${portFlag}\n` + + `Also ensure the iPhone is on iOS 27+, unlocked, and trusted. ` + + `(Override the port with ARGENT_PMD3_TUNNELD_PORT.)` + ); +} + +/** + * Look up the device's RSD endpoint from a running `pymobiledevice3 remote + * tunneld` (its local REST API at 127.0.0.1:). Re-resolved per command so + * a tunneld restart mid-session is picked up without re-creating the service. + */ +async function resolveTunnel(udid: string): Promise { + const port = tunneldPort(); + let payload: Record>; + try { + const res = await fetch(`http://127.0.0.1:${port}/`, { signal: AbortSignal.timeout(4000) }); + // Something other than tunneld could be bound to the port; don't parse an + // error page as a tunnel list. + if (!res.ok) throw new Error(`tunneld responded HTTP ${res.status}`); + payload = (await res.json()) as typeof payload; + } catch (err) { + throw new FailureError( + tunnelHelp(udid, "tunneld is not running"), + { + error_code: FAILURE_CODES.CORE_DEVICE_TUNNEL_UNREACHABLE, + failure_stage: "core_device_resolve_tunnel", + failure_area: "tool_server", + error_kind: "network", + failure_command: "pymobiledevice3", + }, + { cause: err instanceof Error ? err : new Error(String(err)) } + ); + } + const entry = payload?.[udid]; + const t = Array.isArray(entry) ? entry[0] : undefined; + // `!t["tunnel-port"]` (not `== null`) deliberately also rejects 0 — tunneld + // never assigns port 0 to a real tunnel, so treat it the same as missing, + // consistent with the tunnel-address check on the left. + if (!t?.["tunnel-address"] || !t["tunnel-port"]) { + throw new FailureError(tunnelHelp(udid, "no active tunnel is registered for it"), { + error_code: FAILURE_CODES.CORE_DEVICE_TUNNEL_NOT_REGISTERED, + failure_stage: "core_device_resolve_tunnel", + failure_area: "tool_server", + error_kind: "not_found", + failure_command: "pymobiledevice3", + }); + } + return { address: String(t["tunnel-address"]), port: Number(t["tunnel-port"]) }; +} + +/** + * Whether anything is serving on the tunneld port (any HTTP response counts). + * Distinguishes "tunneld not running → start it" from "tunneld running but this + * device's tunnel hasn't formed yet → just wait", so we don't pop a second, + * pointless root prompt (or fail trying to bind an already-used port). + */ +async function isTunneldReachable(port: number): Promise { + try { + await fetch(`http://127.0.0.1:${port}/`, { signal: AbortSignal.timeout(4000) }); + return true; + } catch { + return false; + } +} + +/** + * Return the device's RSD endpoint, auto-starting tunneld via the macOS auth + * modal when it isn't running — so the user never types sudo. Gated behind the + * physical-ios-devices flag (the privileged escalation is opt-in). If tunneld is + * already running but this device's tunnel hasn't registered (e.g. the iPhone is + * locked), it waits rather than popping a second, pointless prompt. + */ +export async function ensureCoreDeviceTunnel(udid: string): Promise { + assertPhysicalIosEnabled(); + try { + return await resolveTunnel(udid); + } catch (notRunning) { + if (process.platform !== "darwin") throw notRunning; + const port = tunneldPort(); + + // Only escalate (root prompt) when tunneld isn't running at all. If it IS + // running, the device just needs to be unlocked/trusted for the handshake — + // don't re-prompt, just poll. + const reachable = await isTunneldReachable(port); + if (!reachable) { + if (!tunnelStartInFlight) { + tunnelStartInFlight = (async () => { + const pmd3Abs = await resolvePmd3Absolute(); + await startTunneldWithPrivilege(pmd3Abs, port); + })(); + } + try { + await tunnelStartInFlight; + } catch (escalationErr) { + tunnelStartInFlight = null; // allow a later retry + throw new FailureError( + tunnelHelp(udid, "the authorization prompt was cancelled or unavailable"), + { + error_code: FAILURE_CODES.CORE_DEVICE_TUNNEL_AUTH_DECLINED, + failure_stage: "core_device_ensure_tunnel_escalation", + failure_area: "tool_server", + error_kind: "subprocess", + failure_command: "pymobiledevice3", + }, + { + cause: + escalationErr instanceof Error ? escalationErr : new Error(String(escalationErr)), + } + ); + } + tunnelStartInFlight = null; + } + + // Poll for this device's tunnel to register (handshake needs it unlocked & trusted). + for (let i = 0; i < 15; i++) { + await sleep(2_000); + try { + return await resolveTunnel(udid); + } catch { + // keep polling + } + } + throw new FailureError( + tunnelHelp( + udid, + reachable + ? "tunneld is running but the device tunnel did not form — is the iPhone unlocked and trusted?" + : "the tunnel did not come up after starting tunneld" + ), + { + error_code: FAILURE_CODES.CORE_DEVICE_TUNNEL_TIMEOUT, + failure_stage: "core_device_ensure_tunnel_poll", + failure_area: "tool_server", + error_kind: "timeout", + failure_command: "pymobiledevice3", + }, + { cause: notRunning instanceof Error ? notRunning : new Error(String(notRunning)) } + ); + } +} + +/** + * Map a CoreDevice agent failure to a FailureError. The agent flags Apple's + * host-input gate (CoreDeviceError 9021) explicitly via `gated9021`: on iOS + * 18-26 the "remote control" services exist but reject touch input, while + * `screen-capture` and hardware buttons (`hid button`) keep working — so this + * message is scoped to tap/swipe, hardware-verified on an iPhone 15 + * (iOS 18.7.8 vs 27.0). + */ +export function agentError(label: string, err: unknown): FailureError { + const cause = err instanceof Error ? err : new Error(String(err)); + if (err instanceof CoreDeviceAgentError && err.gated9021) { + return new FailureError( + `CoreDevice ${label} failed: this iPhone is on an iOS below 27; host-driven touch input ` + + `(tap/swipe) requires iOS 27+. Screenshot and hardware buttons work on earlier iOS ` + + `versions (Apple CoreDeviceError 9021).`, + { + error_code: FAILURE_CODES.CORE_DEVICE_IOS_VERSION_TOO_OLD, + failure_stage: "core_device_command", + failure_area: "tool_server", + error_kind: "unsupported", + failure_command: "pymobiledevice3", + }, + { cause } + ); + } + return new FailureError( + `CoreDevice ${label} failed: ${cause.message.slice(0, 240)}`, + { + error_code: FAILURE_CODES.CORE_DEVICE_COMMAND_FAILED, + failure_stage: "core_device_command", + failure_area: "tool_server", + error_kind: "subprocess", + failure_command: "pymobiledevice3", + }, + { cause } + ); +} + +/** Normalized 0..1 → the device's 0..65535 HID coordinate space. */ +export function toHid(v: number): number { + return Math.max(0, Math.min(65535, Math.round(v * 65535))); +} + +/** + * Derive the pmd3 `drag` parameters for a swipe of `durationMs`. A drag must + * dwell to register, so degenerate inputs are clamped: 0ms is dropped by iOS + * like a zero-dwell tap, and a negative value would reach pmd3 as a flag-like + * "-0.100" arg; both floor to 50ms. A pathological value is capped at 60s so it + * can't pin the device for minutes. The command timeout scales with the drag so + * a long swipe isn't SIGTERM-killed mid-gesture (pmd3 runs for ~`dur` ms; the + * +15s buffer covers the interpreter's startup). + */ +export function swipeDragParams(durationMs: number): { + steps: number; + seconds: string; + timeoutMs: number; +} { + const dur = Math.max(50, Math.min(60_000, Math.round(durationMs))); + const steps = Math.max(2, Math.min(60, Math.round(dur / 16))); + return { steps, seconds: (dur / 1000).toFixed(3), timeoutMs: dur + 15_000 }; +} + +/** + * Ensure the personalized DeveloperDiskImage is mounted (the CoreDevice services + * live in it). Idempotent: when already mounted, pymobiledevice3 exits non-zero + * with "already mounted", which we treat as success. CoreDevice usually mounts + * it automatically when the device connects, so this is a best-effort fallback — + * a genuine mount failure surfaces later as an actionable per-command error. + */ +async function ensureMounted(pmd3: string, rsd: Rsd): Promise { + try { + await execFileAsync(pmd3, ["mounter", "auto-mount", "--rsd", rsd.address, String(rsd.port)], { + timeout: 60_000, + }); + } catch { + // Best-effort: when the DDI is already mounted pymobiledevice3 exits non-zero + // with "already mounted"; a genuine mount failure surfaces later as an + // actionable per-command error. Either way, don't fail service creation here. + } +} + +export const coreDeviceBlueprint: ServiceBlueprint = { + namespace: CORE_DEVICE_NAMESPACE, + getURN(device: DeviceInfo) { + return `${CORE_DEVICE_NAMESPACE}:${device.id}`; + }, + async factory(_deps, _payload, options) { + const opts = options as unknown as CoreDeviceFactoryOptions | undefined; + const device = opts?.device; + if (!device?.id) { + throw new FailureError( + `${CORE_DEVICE_NAMESPACE}.factory requires a resolved DeviceInfo via options.device. ` + + `Use coreDeviceRef(device) when registering the service ref.`, + { + error_code: FAILURE_CODES.CORE_DEVICE_FACTORY_OPTIONS_MISSING, + failure_stage: "core_device_factory_options", + failure_area: "tool_server", + error_kind: "validation", + } + ); + } + if (device.platform !== "ios" || device.kind !== "device") { + throw new FailureError( + `${CORE_DEVICE_NAMESPACE} only drives physical iOS devices; got ${device.platform}/${device.kind}.`, + { + error_code: FAILURE_CODES.CORE_DEVICE_WRONG_DEVICE, + failure_stage: "core_device_factory_platform", + failure_area: "tool_server", + error_kind: "validation", + } + ); + } + // Gate first — before any setup probe — so a flag-disabled user gets the + // "enable the flag" message rather than an incidental "install pymobiledevice3" + // (ensureCoreDeviceTunnel re-checks this; the duplicate keeps the message + // correct regardless of whether pmd3 happens to be installed). + assertPhysicalIosEnabled(); + + const pmd3 = resolvePmd3(); + const udid = device.id; + + // Surface setup problems up front, in the order a user fixes them: install + // pymobiledevice3, then ensure the tunnel (auto-started via the macOS auth + // modal if needed — no manual sudo), then mount the DDI. + await verifyPmd3Available(pmd3); + const rsd = await ensureCoreDeviceTunnel(udid); + await ensureMounted(pmd3, rsd); + + // Resolve the interpreter that has pymobiledevice3 importable (the CLI's own + // venv python) and materialize the agent program, then start ONE long-lived + // process for this device: it connects the RSD tunnel and opens the HID / + // screenshot services once, so each interaction is a socket write instead of + // a fresh ~0.8s Python cold-start (~0.5s of which is just the pmd3 import). + const python = await resolvePmd3Python(await resolvePmd3Absolute()); + const scriptPath = await materializeAgentScript(); + const agent = new CoreDeviceAgent(python, scriptPath, udid, tunneldPort()); + await agent.start(); + + const call = async ( + label: string, + op: string, + args: Record = {}, + timeoutMs = 30_000 + ): Promise> => { + try { + return (await agent.request(op, args, timeoutMs)) as Record; + } catch (err) { + throw agentError(label, err); + } + }; + + const events = new TypedEventEmitter(); + + const api: CoreDeviceApi = { + async screenshot() { + const res = await call("screenshot", "screenshot", {}, 30_000); + const b64 = typeof res.image_b64 === "string" ? res.image_b64 : ""; + const path = join(tmpdir(), `argent-ios-shot-${randomUUID()}.png`); + await writeFile(path, Buffer.from(b64, "base64")); + return { path }; + }, + async tap(x, y) { + // A zero-duration tap is dropped by iOS; the agent emits a short held + // dwell-drag. We hand it the 0..65535 HID coordinate. + await call("tap", "tap", { x: toHid(x), y: toHid(y) }, 15_000); + }, + async swipe(fromX, fromY, toX, toY, durationMs) { + const { steps, seconds, timeoutMs } = swipeDragParams(durationMs); + await call( + "swipe", + "swipe", + { + x1: toHid(fromX), + y1: toHid(fromY), + x2: toHid(toX), + y2: toHid(toY), + steps, + duration: Number(seconds), + }, + timeoutMs + ); + }, + async button(name) { + await call("button", "button", { name }, 15_000); + }, + async axtree() { + // The audit walk + rect pass runs on-device; give it room. + const res = await call("axtree", "axtree", {}, 45_000); + return { + elements: (res.elements as CoreDeviceAxTree["elements"]) ?? [], + screen: res.screen as CoreDeviceAxTree["screen"], + }; + }, + }; + + const instance: ServiceInstance = { + api, + // One persistent pymobiledevice3 process per device — tear it down. + dispose: async () => { + agent.dispose(); + }, + events, + }; + return instance; + }, +}; diff --git a/packages/tool-server/src/blueprints/coredevice-agent.py b/packages/tool-server/src/blueprints/coredevice-agent.py new file mode 100644 index 000000000..974e15b11 --- /dev/null +++ b/packages/tool-server/src/blueprints/coredevice-agent.py @@ -0,0 +1,288 @@ +#!/usr/bin/env python3 +"""Persistent CoreDevice agent — one long-lived process per physical iPhone. + +Replaces per-call `pymobiledevice3` CLI spawns (each ~0.8s, ~0.5s just the +Python import) with a single process that connects the RSD tunnel once, holds +the touchscreen media-stream session + screenshot service open, and executes +newline-delimited JSON commands on stdin, replying with one JSON line each. + +Reuses pymobiledevice3's own CLI helpers so behaviour is identical to the +`developer core-device …` commands (dwell-drag tap, mainTouchscreen reports, +Indigo hardware buttons, screen-capture PNG). + +Protocol (one JSON object per line): + <- {"udid": "...", "port": 49151} (argv, not stdin) + -> {"ready": true} (or {"ready": false, "error": "..."}) + <- {"id": 1, "op": "screenshot"} + -> {"id": 1, "ok": true, "image_b64": "..."} + <- {"id": 2, "op": "tap", "x": 32768, "y": 20000} (x/y already 0..65535) + <- {"id": 3, "op": "swipe", "x1":.., "y1":.., "x2":.., "y2":.., "steps":19, "duration":0.3} + <- {"id": 4, "op": "button", "name": "home"} + <- {"id": 5, "op": "homescreen"} (springboard icon grid) + -> {"id": N, "ok": true, ...} | {"id": N, "error": "...", "gated_9021": bool} +""" +import asyncio +import base64 +import contextlib +import json +import sys + +from pymobiledevice3.remote.remote_service_discovery import RemoteServiceDiscoveryService +from pymobiledevice3.remote.core_device.hid_service import ( + touch_session, + IndigoHIDService, + DIGITIZER_SURFACE_MAIN_TOUCHSCREEN, +) +from pymobiledevice3.remote.core_device.screen_capture_service import ScreenCaptureService +from pymobiledevice3.services.springboard import SpringBoardServicesService +from pymobiledevice3.cli.developer.core_device import _do_drag, _send_button_press, _NAMED_BUTTONS +from pymobiledevice3.dtx_service_provider import DtxServiceProvider +from pymobiledevice3.dtx.connection import DTXConnection +from pymobiledevice3.services.accessibilityaudit import AccessibilityAudit, deserialize_object + +import urllib.request + + +# --- RSDCheckin fix for the iOS-26+ accessibility (axAudit) DTX service -------- +# In iOS 26 Apple re-plumbed axauditd onto RemoteServiceDiscovery; the +# `…axAuditDaemon.remoteserver.shim.remote` DTX daemon now requires the RSDCheckin +# handshake before it accepts DTX framing. pymobiledevice3's DtxServiceProvider +# opens RSD services with a raw connect (create_service_connection) and skips +# RSDCheckin, so iOS 26/27 drops the connection on the first byte. start_lockdown_service +# performs RSDCheckin — route RSD DTX opens through it. (ref: littledivy/iphone-mirroring-axdump) +_orig_open_dtx = DtxServiceProvider._open_dtx_connection + + +async def _open_dtx_with_checkin(self, service_name, *, strip_ssl=False): + if isinstance(self.lockdown, RemoteServiceDiscoveryService): + svc = await self.lockdown.start_lockdown_service(service_name) + return DTXConnection(svc.reader, svc.writer) + return await _orig_open_dtx(self, service_name, strip_ssl=strip_ssl) + + +DtxServiceProvider._open_dtx_connection = _open_dtx_with_checkin + + +def _resolve_rsd(udid: str, port: int): + payload = json.load(urllib.request.urlopen(f"http://127.0.0.1:{port}/", timeout=4)) + entry = payload.get(udid) or [] + t = entry[0] if entry else {} + addr, tport = t.get("tunnel-address"), t.get("tunnel-port") + if not addr or not tport: + raise RuntimeError(f"no active tunnel registered for {udid} on tunneld :{port}") + return addr, int(tport) + + +async def _maybe_await(v): + return await v if asyncio.iscoroutine(v) else v + + +class Agent: + def __init__(self, udid: str, port: int): + self.udid = udid + self.port = port + self.rsd = None + self.stack = contextlib.AsyncExitStack() + self.touch = None # UniversalHIDServiceService, held open (media stream stays warm) + + async def connect(self): + addr, tport = _resolve_rsd(self.udid, self.port) + self.rsd = RemoteServiceDiscoveryService((addr, tport)) + await self.rsd.connect() + + async def _ensure_touch(self): + # Lazily open (and keep) the touchscreen media-stream session — the auth + # gate backboardd needs for injected touches. Kept warm so taps don't pay + # the media-stream startup each time. + if self.touch is None: + self.touch = await self.stack.enter_async_context(touch_session(self.rsd)) + return self.touch + + async def op_screenshot(self, _): + # ScreenCaptureService delivers one PNG per open (the stream ends after + # the frame), so open a fresh one each call — cheap now the interpreter + # and tunnel are already warm. + async with ScreenCaptureService(self.rsd) as screen: + resp = await screen.capture_screenshot() + return {"image_b64": base64.b64encode(resp["image"]).decode("ascii")} + + async def op_tap(self, msg): + svc = await self._ensure_touch() + x, y = int(msg["x"]), int(msg["y"]) + # Zero-dwell taps are dropped by iOS; emit a short held drag with a tiny + # move away from the edge (mirrors core-device.ts / the CLI drag path). + y2 = y + 96 if y <= 65535 - 120 else y - 96 + await _do_drag(svc, x, y, x, y2, 3, 0.15, tsid=DIGITIZER_SURFACE_MAIN_TOUCHSCREEN) + return {} + + async def op_swipe(self, msg): + svc = await self._ensure_touch() + await _do_drag( + svc, int(msg["x1"]), int(msg["y1"]), int(msg["x2"]), int(msg["y2"]), + int(msg.get("steps", 19)), float(msg.get("duration", 0.3)), + tsid=DIGITIZER_SURFACE_MAIN_TOUCHSCREEN, + ) + return {} + + async def op_button(self, msg): + name = msg["name"] + if name not in _NAMED_BUTTONS: + raise RuntimeError(f"unknown button '{name}'") + usage_page, usage_code, hold = _NAMED_BUTTONS[name] + async with IndigoHIDService(self.rsd) as svc: + await _send_button_press(svc, usage_page, usage_code, "press", hold) + return {} + + async def op_homescreen(self, _): + sb = SpringBoardServicesService(lockdown=self.rsd) + icons = await _maybe_await(sb.get_icon_state()) + metrics = await _maybe_await(sb.get_homescreen_icon_metrics()) + return {"icon_state": icons, "metrics": metrics} + + async def op_axtree(self, msg): + """The on-screen accessibility tree of whatever app is frontmost (or the + home screen), via the iOS-26+ axAudit service (RSDCheckin-unlocked above). + + Returns each element's accessible caption (label + value + traits) in + VoiceOver reading order plus a stable element id, and — where the + accessibility audit reports one — its on-screen rect in points. Per-element + geometry isn't exposed on hardware, so only audited elements carry a rect; + the TS layer fills the gaps. Screen size (points) comes from SpringBoard. + """ + limit = int(msg.get("limit", 120)) + + # 1) Walk the elements (caption + reading order + element id). + walk = AccessibilityAudit(self.rsd) + elements = [] + try: + async for el in walk.iter_elements(): + elements.append( + {"caption": el.caption, "id": el.element.identifier.hex()} + ) + if len(elements) >= limit: + break + finally: + with contextlib.suppress(Exception): + await walk.close() + + # 2) Audit for rects, keyed by element id (the only hardware frame source). + rects = {} + audit = AccessibilityAudit(self.rsd) + try: + types = await audit.supported_audits_types() + await audit._ensure_ready() + await audit._invoke("deviceBeginAuditTypes:", types, expects_reply=False) + while True: + name, args = await audit._event_queue.get() + if name != "hostDeviceDidCompleteAuditCategoriesWithAuditIssues:": + continue + issues = deserialize_object(audit._extract_event_payload(args)) + # iOS 27 returns the AXAuditIssue list directly; older wrap it in [{"value":…}]. + if ( + isinstance(issues, list) + and issues + and isinstance(issues[0], dict) + and "value" in issues[0] + ): + issues = issues[0]["value"] + for iss in issues or []: + try: + eid = iss._fields["AuditElementValue_v1"]._fields[ + "PlatformElementValue_v1" + ].hex() + rect = iss._fields.get("ElementRectValue_v1") + if eid and rect and eid not in rects: + rects[eid] = rect + except Exception: + continue + break + except Exception: + pass # audit is best-effort; the tree still returns without rects + finally: + with contextlib.suppress(Exception): + await audit.close() + + for el in elements: + if el["id"] in rects: + el["rect"] = rects[el["id"]] + + # 3) Screen size in points, for normalizing the rects. + screen = None + with contextlib.suppress(Exception): + sb = SpringBoardServicesService(lockdown=self.rsd) + m = await _maybe_await(sb.get_homescreen_icon_metrics()) + screen = {"w": m.get("homeScreenWidth"), "h": m.get("homeScreenHeight")} + + return {"elements": elements, "screen": screen} + + async def op_ping(self, _): + return {"pong": True} + + async def dispatch(self, msg): + op = msg.get("op") + fn = getattr(self, f"op_{op}", None) + if fn is None: + raise RuntimeError(f"unknown op '{op}'") + return await fn(msg) + + async def close(self): + with contextlib.suppress(Exception): + await self.stack.aclose() + if self.rsd is not None: + with contextlib.suppress(Exception): + await self.rsd.close() + + +def _is_9021(text: str) -> bool: + import re + return bool(re.search(r"core\s*device\s*error\W*9021", text, re.I) or re.search(r"\b9021\b", text)) + + +async def main(): + udid = sys.argv[1] + port = int(sys.argv[2]) if len(sys.argv) > 2 else 49151 + agent = Agent(udid, port) + out = sys.stdout + + def emit(obj): + # default=str keeps a stray plist type (bytes/datetime in springboard + # icon state) from crashing serialization mid-session. + out.write(json.dumps(obj, default=str) + "\n") + out.flush() + + try: + await agent.connect() + except Exception as e: # noqa: BLE001 + emit({"ready": False, "error": str(e)}) + return + emit({"ready": True}) + + loop = asyncio.get_event_loop() + reader = asyncio.StreamReader() + await loop.connect_read_pipe(lambda: asyncio.StreamReaderProtocol(reader), sys.stdin) + + while True: + line = await reader.readline() + if not line: + break + line = line.strip() + if not line: + continue + try: + msg = json.loads(line) + except Exception: # noqa: BLE001 + emit({"error": "bad json"}) + continue + mid = msg.get("id") + try: + result = await agent.dispatch(msg) + emit({"id": mid, "ok": True, **result}) + except Exception as e: # noqa: BLE001 + text = f"{type(e).__name__}: {e}" + emit({"id": mid, "error": text, "gated_9021": _is_9021(text)}) + + await agent.close() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/packages/tool-server/src/blueprints/coredevice-agent.ts b/packages/tool-server/src/blueprints/coredevice-agent.ts new file mode 100644 index 000000000..b44260bdd --- /dev/null +++ b/packages/tool-server/src/blueprints/coredevice-agent.ts @@ -0,0 +1,252 @@ +import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process"; +import { readFile, writeFile } from "node:fs/promises"; +import { existsSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join, dirname } from "node:path"; +import { createHash } from "node:crypto"; +import { createInterface, type Interface } from "node:readline"; + +/** + * The persistent CoreDevice agent (a small pymobiledevice3 program) that a + * physical iPhone is driven through. It is base64-embedded rather than shipped + * as a loose `.py` so it survives esbuild bundling with zero build-pipeline + * wiring, and base64 (not a template literal) so its Python `\s`/`\n` escapes + * can't be mangled by JS string parsing. + * + * Source of truth: `coredevice-agent.py.b64` sits next to this file; regenerate + * with `base64 -i coredevice-agent.py | tr -d '\n'`. The decoded script speaks + * newline-delimited JSON on stdio (see AGENT_PROTOCOL below). + */ +const AGENT_SCRIPT_B64 = + "IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwoiIiJQZXJzaXN0ZW50IENvcmVEZXZpY2UgYWdlbnQg4oCUIG9uZSBsb25nLWxpdmVkIHByb2Nlc3MgcGVyIHBoeXNpY2FsIGlQaG9uZS4KClJlcGxhY2VzIHBlci1jYWxsIGBweW1vYmlsZWRldmljZTNgIENMSSBzcGF3bnMgKGVhY2ggfjAuOHMsIH4wLjVzIGp1c3QgdGhlClB5dGhvbiBpbXBvcnQpIHdpdGggYSBzaW5nbGUgcHJvY2VzcyB0aGF0IGNvbm5lY3RzIHRoZSBSU0QgdHVubmVsIG9uY2UsIGhvbGRzCnRoZSB0b3VjaHNjcmVlbiBtZWRpYS1zdHJlYW0gc2Vzc2lvbiArIHNjcmVlbnNob3Qgc2VydmljZSBvcGVuLCBhbmQgZXhlY3V0ZXMKbmV3bGluZS1kZWxpbWl0ZWQgSlNPTiBjb21tYW5kcyBvbiBzdGRpbiwgcmVwbHlpbmcgd2l0aCBvbmUgSlNPTiBsaW5lIGVhY2guCgpSZXVzZXMgcHltb2JpbGVkZXZpY2UzJ3Mgb3duIENMSSBoZWxwZXJzIHNvIGJlaGF2aW91ciBpcyBpZGVudGljYWwgdG8gdGhlCmBkZXZlbG9wZXIgY29yZS1kZXZpY2Ug4oCmYCBjb21tYW5kcyAoZHdlbGwtZHJhZyB0YXAsIG1haW5Ub3VjaHNjcmVlbiByZXBvcnRzLApJbmRpZ28gaGFyZHdhcmUgYnV0dG9ucywgc2NyZWVuLWNhcHR1cmUgUE5HKS4KClByb3RvY29sIChvbmUgSlNPTiBvYmplY3QgcGVyIGxpbmUpOgogIDwtIHsidWRpZCI6ICIuLi4iLCAicG9ydCI6IDQ5MTUxfSAgICAgICAgICAgICAgICAgKGFyZ3YsIG5vdCBzdGRpbikKICAtPiB7InJlYWR5IjogdHJ1ZX0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAob3IgeyJyZWFkeSI6IGZhbHNlLCAiZXJyb3IiOiAiLi4uIn0pCiAgPC0geyJpZCI6IDEsICJvcCI6ICJzY3JlZW5zaG90In0KICAtPiB7ImlkIjogMSwgIm9rIjogdHJ1ZSwgImltYWdlX2I2NCI6ICIuLi4ifQogIDwtIHsiaWQiOiAyLCAib3AiOiAidGFwIiwgIngiOiAzMjc2OCwgInkiOiAyMDAwMH0gICAgICAgICAgKHgveSBhbHJlYWR5IDAuLjY1NTM1KQogIDwtIHsiaWQiOiAzLCAib3AiOiAic3dpcGUiLCAieDEiOi4uLCAieTEiOi4uLCAieDIiOi4uLCAieTIiOi4uLCAic3RlcHMiOjE5LCAiZHVyYXRpb24iOjAuM30KICA8LSB7ImlkIjogNCwgIm9wIjogImJ1dHRvbiIsICJuYW1lIjogImhvbWUifQogIDwtIHsiaWQiOiA1LCAib3AiOiAiaG9tZXNjcmVlbiJ9ICAgICAgICAgICAgICAgICAgICAgICAgICAgIChzcHJpbmdib2FyZCBpY29uIGdyaWQpCiAgLT4geyJpZCI6IE4sICJvayI6IHRydWUsIC4uLn0gIHwgIHsiaWQiOiBOLCAiZXJyb3IiOiAiLi4uIiwgImdhdGVkXzkwMjEiOiBib29sfQoiIiIKaW1wb3J0IGFzeW5jaW8KaW1wb3J0IGJhc2U2NAppbXBvcnQgY29udGV4dGxpYgppbXBvcnQganNvbgppbXBvcnQgc3lzCgpmcm9tIHB5bW9iaWxlZGV2aWNlMy5yZW1vdGUucmVtb3RlX3NlcnZpY2VfZGlzY292ZXJ5IGltcG9ydCBSZW1vdGVTZXJ2aWNlRGlzY292ZXJ5U2VydmljZQpmcm9tIHB5bW9iaWxlZGV2aWNlMy5yZW1vdGUuY29yZV9kZXZpY2UuaGlkX3NlcnZpY2UgaW1wb3J0ICgKICAgIHRvdWNoX3Nlc3Npb24sCiAgICBJbmRpZ29ISURTZXJ2aWNlLAogICAgRElHSVRJWkVSX1NVUkZBQ0VfTUFJTl9UT1VDSFNDUkVFTiwKKQpmcm9tIHB5bW9iaWxlZGV2aWNlMy5yZW1vdGUuY29yZV9kZXZpY2Uuc2NyZWVuX2NhcHR1cmVfc2VydmljZSBpbXBvcnQgU2NyZWVuQ2FwdHVyZVNlcnZpY2UKZnJvbSBweW1vYmlsZWRldmljZTMuc2VydmljZXMuc3ByaW5nYm9hcmQgaW1wb3J0IFNwcmluZ0JvYXJkU2VydmljZXNTZXJ2aWNlCmZyb20gcHltb2JpbGVkZXZpY2UzLmNsaS5kZXZlbG9wZXIuY29yZV9kZXZpY2UgaW1wb3J0IF9kb19kcmFnLCBfc2VuZF9idXR0b25fcHJlc3MsIF9OQU1FRF9CVVRUT05TCmZyb20gcHltb2JpbGVkZXZpY2UzLmR0eF9zZXJ2aWNlX3Byb3ZpZGVyIGltcG9ydCBEdHhTZXJ2aWNlUHJvdmlkZXIKZnJvbSBweW1vYmlsZWRldmljZTMuZHR4LmNvbm5lY3Rpb24gaW1wb3J0IERUWENvbm5lY3Rpb24KZnJvbSBweW1vYmlsZWRldmljZTMuc2VydmljZXMuYWNjZXNzaWJpbGl0eWF1ZGl0IGltcG9ydCBBY2Nlc3NpYmlsaXR5QXVkaXQsIGRlc2VyaWFsaXplX29iamVjdAoKaW1wb3J0IHVybGxpYi5yZXF1ZXN0CgoKIyAtLS0gUlNEQ2hlY2tpbiBmaXggZm9yIHRoZSBpT1MtMjYrIGFjY2Vzc2liaWxpdHkgKGF4QXVkaXQpIERUWCBzZXJ2aWNlIC0tLS0tLS0tCiMgSW4gaU9TIDI2IEFwcGxlIHJlLXBsdW1iZWQgYXhhdWRpdGQgb250byBSZW1vdGVTZXJ2aWNlRGlzY292ZXJ5OyB0aGUKIyBg4oCmYXhBdWRpdERhZW1vbi5yZW1vdGVzZXJ2ZXIuc2hpbS5yZW1vdGVgIERUWCBkYWVtb24gbm93IHJlcXVpcmVzIHRoZSBSU0RDaGVja2luCiMgaGFuZHNoYWtlIGJlZm9yZSBpdCBhY2NlcHRzIERUWCBmcmFtaW5nLiBweW1vYmlsZWRldmljZTMncyBEdHhTZXJ2aWNlUHJvdmlkZXIKIyBvcGVucyBSU0Qgc2VydmljZXMgd2l0aCBhIHJhdyBjb25uZWN0IChjcmVhdGVfc2VydmljZV9jb25uZWN0aW9uKSBhbmQgc2tpcHMKIyBSU0RDaGVja2luLCBzbyBpT1MgMjYvMjcgZHJvcHMgdGhlIGNvbm5lY3Rpb24gb24gdGhlIGZpcnN0IGJ5dGUuIHN0YXJ0X2xvY2tkb3duX3NlcnZpY2UKIyBwZXJmb3JtcyBSU0RDaGVja2luIOKAlCByb3V0ZSBSU0QgRFRYIG9wZW5zIHRocm91Z2ggaXQuIChyZWY6IGxpdHRsZWRpdnkvaXBob25lLW1pcnJvcmluZy1heGR1bXApCl9vcmlnX29wZW5fZHR4ID0gRHR4U2VydmljZVByb3ZpZGVyLl9vcGVuX2R0eF9jb25uZWN0aW9uCgoKYXN5bmMgZGVmIF9vcGVuX2R0eF93aXRoX2NoZWNraW4oc2VsZiwgc2VydmljZV9uYW1lLCAqLCBzdHJpcF9zc2w9RmFsc2UpOgogICAgaWYgaXNpbnN0YW5jZShzZWxmLmxvY2tkb3duLCBSZW1vdGVTZXJ2aWNlRGlzY292ZXJ5U2VydmljZSk6CiAgICAgICAgc3ZjID0gYXdhaXQgc2VsZi5sb2NrZG93bi5zdGFydF9sb2NrZG93bl9zZXJ2aWNlKHNlcnZpY2VfbmFtZSkKICAgICAgICByZXR1cm4gRFRYQ29ubmVjdGlvbihzdmMucmVhZGVyLCBzdmMud3JpdGVyKQogICAgcmV0dXJuIGF3YWl0IF9vcmlnX29wZW5fZHR4KHNlbGYsIHNlcnZpY2VfbmFtZSwgc3RyaXBfc3NsPXN0cmlwX3NzbCkKCgpEdHhTZXJ2aWNlUHJvdmlkZXIuX29wZW5fZHR4X2Nvbm5lY3Rpb24gPSBfb3Blbl9kdHhfd2l0aF9jaGVja2luCgoKZGVmIF9yZXNvbHZlX3JzZCh1ZGlkOiBzdHIsIHBvcnQ6IGludCk6CiAgICBwYXlsb2FkID0ganNvbi5sb2FkKHVybGxpYi5yZXF1ZXN0LnVybG9wZW4oZiJodHRwOi8vMTI3LjAuMC4xOntwb3J0fS8iLCB0aW1lb3V0PTQpKQogICAgZW50cnkgPSBwYXlsb2FkLmdldCh1ZGlkKSBvciBbXQogICAgdCA9IGVudHJ5WzBdIGlmIGVudHJ5IGVsc2Uge30KICAgIGFkZHIsIHRwb3J0ID0gdC5nZXQoInR1bm5lbC1hZGRyZXNzIiksIHQuZ2V0KCJ0dW5uZWwtcG9ydCIpCiAgICBpZiBub3QgYWRkciBvciBub3QgdHBvcnQ6CiAgICAgICAgcmFpc2UgUnVudGltZUVycm9yKGYibm8gYWN0aXZlIHR1bm5lbCByZWdpc3RlcmVkIGZvciB7dWRpZH0gb24gdHVubmVsZCA6e3BvcnR9IikKICAgIHJldHVybiBhZGRyLCBpbnQodHBvcnQpCgoKYXN5bmMgZGVmIF9tYXliZV9hd2FpdCh2KToKICAgIHJldHVybiBhd2FpdCB2IGlmIGFzeW5jaW8uaXNjb3JvdXRpbmUodikgZWxzZSB2CgoKY2xhc3MgQWdlbnQ6CiAgICBkZWYgX19pbml0X18oc2VsZiwgdWRpZDogc3RyLCBwb3J0OiBpbnQpOgogICAgICAgIHNlbGYudWRpZCA9IHVkaWQKICAgICAgICBzZWxmLnBvcnQgPSBwb3J0CiAgICAgICAgc2VsZi5yc2QgPSBOb25lCiAgICAgICAgc2VsZi5zdGFjayA9IGNvbnRleHRsaWIuQXN5bmNFeGl0U3RhY2soKQogICAgICAgIHNlbGYudG91Y2ggPSBOb25lICAjIFVuaXZlcnNhbEhJRFNlcnZpY2VTZXJ2aWNlLCBoZWxkIG9wZW4gKG1lZGlhIHN0cmVhbSBzdGF5cyB3YXJtKQoKICAgIGFzeW5jIGRlZiBjb25uZWN0KHNlbGYpOgogICAgICAgIGFkZHIsIHRwb3J0ID0gX3Jlc29sdmVfcnNkKHNlbGYudWRpZCwgc2VsZi5wb3J0KQogICAgICAgIHNlbGYucnNkID0gUmVtb3RlU2VydmljZURpc2NvdmVyeVNlcnZpY2UoKGFkZHIsIHRwb3J0KSkKICAgICAgICBhd2FpdCBzZWxmLnJzZC5jb25uZWN0KCkKCiAgICBhc3luYyBkZWYgX2Vuc3VyZV90b3VjaChzZWxmKToKICAgICAgICAjIExhemlseSBvcGVuIChhbmQga2VlcCkgdGhlIHRvdWNoc2NyZWVuIG1lZGlhLXN0cmVhbSBzZXNzaW9uIOKAlCB0aGUgYXV0aAogICAgICAgICMgZ2F0ZSBiYWNrYm9hcmRkIG5lZWRzIGZvciBpbmplY3RlZCB0b3VjaGVzLiBLZXB0IHdhcm0gc28gdGFwcyBkb24ndCBwYXkKICAgICAgICAjIHRoZSBtZWRpYS1zdHJlYW0gc3RhcnR1cCBlYWNoIHRpbWUuCiAgICAgICAgaWYgc2VsZi50b3VjaCBpcyBOb25lOgogICAgICAgICAgICBzZWxmLnRvdWNoID0gYXdhaXQgc2VsZi5zdGFjay5lbnRlcl9hc3luY19jb250ZXh0KHRvdWNoX3Nlc3Npb24oc2VsZi5yc2QpKQogICAgICAgIHJldHVybiBzZWxmLnRvdWNoCgogICAgYXN5bmMgZGVmIG9wX3NjcmVlbnNob3Qoc2VsZiwgXyk6CiAgICAgICAgIyBTY3JlZW5DYXB0dXJlU2VydmljZSBkZWxpdmVycyBvbmUgUE5HIHBlciBvcGVuICh0aGUgc3RyZWFtIGVuZHMgYWZ0ZXIKICAgICAgICAjIHRoZSBmcmFtZSksIHNvIG9wZW4gYSBmcmVzaCBvbmUgZWFjaCBjYWxsIOKAlCBjaGVhcCBub3cgdGhlIGludGVycHJldGVyCiAgICAgICAgIyBhbmQgdHVubmVsIGFyZSBhbHJlYWR5IHdhcm0uCiAgICAgICAgYXN5bmMgd2l0aCBTY3JlZW5DYXB0dXJlU2VydmljZShzZWxmLnJzZCkgYXMgc2NyZWVuOgogICAgICAgICAgICByZXNwID0gYXdhaXQgc2NyZWVuLmNhcHR1cmVfc2NyZWVuc2hvdCgpCiAgICAgICAgcmV0dXJuIHsiaW1hZ2VfYjY0IjogYmFzZTY0LmI2NGVuY29kZShyZXNwWyJpbWFnZSJdKS5kZWNvZGUoImFzY2lpIil9CgogICAgYXN5bmMgZGVmIG9wX3RhcChzZWxmLCBtc2cpOgogICAgICAgIHN2YyA9IGF3YWl0IHNlbGYuX2Vuc3VyZV90b3VjaCgpCiAgICAgICAgeCwgeSA9IGludChtc2dbIngiXSksIGludChtc2dbInkiXSkKICAgICAgICAjIFplcm8tZHdlbGwgdGFwcyBhcmUgZHJvcHBlZCBieSBpT1M7IGVtaXQgYSBzaG9ydCBoZWxkIGRyYWcgd2l0aCBhIHRpbnkKICAgICAgICAjIG1vdmUgYXdheSBmcm9tIHRoZSBlZGdlIChtaXJyb3JzIGNvcmUtZGV2aWNlLnRzIC8gdGhlIENMSSBkcmFnIHBhdGgpLgogICAgICAgIHkyID0geSArIDk2IGlmIHkgPD0gNjU1MzUgLSAxMjAgZWxzZSB5IC0gOTYKICAgICAgICBhd2FpdCBfZG9fZHJhZyhzdmMsIHgsIHksIHgsIHkyLCAzLCAwLjE1LCB0c2lkPURJR0lUSVpFUl9TVVJGQUNFX01BSU5fVE9VQ0hTQ1JFRU4pCiAgICAgICAgcmV0dXJuIHt9CgogICAgYXN5bmMgZGVmIG9wX3N3aXBlKHNlbGYsIG1zZyk6CiAgICAgICAgc3ZjID0gYXdhaXQgc2VsZi5fZW5zdXJlX3RvdWNoKCkKICAgICAgICBhd2FpdCBfZG9fZHJhZygKICAgICAgICAgICAgc3ZjLCBpbnQobXNnWyJ4MSJdKSwgaW50KG1zZ1sieTEiXSksIGludChtc2dbIngyIl0pLCBpbnQobXNnWyJ5MiJdKSwKICAgICAgICAgICAgaW50KG1zZy5nZXQoInN0ZXBzIiwgMTkpKSwgZmxvYXQobXNnLmdldCgiZHVyYXRpb24iLCAwLjMpKSwKICAgICAgICAgICAgdHNpZD1ESUdJVElaRVJfU1VSRkFDRV9NQUlOX1RPVUNIU0NSRUVOLAogICAgICAgICkKICAgICAgICByZXR1cm4ge30KCiAgICBhc3luYyBkZWYgb3BfYnV0dG9uKHNlbGYsIG1zZyk6CiAgICAgICAgbmFtZSA9IG1zZ1sibmFtZSJdCiAgICAgICAgaWYgbmFtZSBub3QgaW4gX05BTUVEX0JVVFRPTlM6CiAgICAgICAgICAgIHJhaXNlIFJ1bnRpbWVFcnJvcihmInVua25vd24gYnV0dG9uICd7bmFtZX0nIikKICAgICAgICB1c2FnZV9wYWdlLCB1c2FnZV9jb2RlLCBob2xkID0gX05BTUVEX0JVVFRPTlNbbmFtZV0KICAgICAgICBhc3luYyB3aXRoIEluZGlnb0hJRFNlcnZpY2Uoc2VsZi5yc2QpIGFzIHN2YzoKICAgICAgICAgICAgYXdhaXQgX3NlbmRfYnV0dG9uX3ByZXNzKHN2YywgdXNhZ2VfcGFnZSwgdXNhZ2VfY29kZSwgInByZXNzIiwgaG9sZCkKICAgICAgICByZXR1cm4ge30KCiAgICBhc3luYyBkZWYgb3BfaG9tZXNjcmVlbihzZWxmLCBfKToKICAgICAgICBzYiA9IFNwcmluZ0JvYXJkU2VydmljZXNTZXJ2aWNlKGxvY2tkb3duPXNlbGYucnNkKQogICAgICAgIGljb25zID0gYXdhaXQgX21heWJlX2F3YWl0KHNiLmdldF9pY29uX3N0YXRlKCkpCiAgICAgICAgbWV0cmljcyA9IGF3YWl0IF9tYXliZV9hd2FpdChzYi5nZXRfaG9tZXNjcmVlbl9pY29uX21ldHJpY3MoKSkKICAgICAgICByZXR1cm4geyJpY29uX3N0YXRlIjogaWNvbnMsICJtZXRyaWNzIjogbWV0cmljc30KCiAgICBhc3luYyBkZWYgb3BfYXh0cmVlKHNlbGYsIG1zZyk6CiAgICAgICAgIiIiVGhlIG9uLXNjcmVlbiBhY2Nlc3NpYmlsaXR5IHRyZWUgb2Ygd2hhdGV2ZXIgYXBwIGlzIGZyb250bW9zdCAob3IgdGhlCiAgICAgICAgaG9tZSBzY3JlZW4pLCB2aWEgdGhlIGlPUy0yNisgYXhBdWRpdCBzZXJ2aWNlIChSU0RDaGVja2luLXVubG9ja2VkIGFib3ZlKS4KCiAgICAgICAgUmV0dXJucyBlYWNoIGVsZW1lbnQncyBhY2Nlc3NpYmxlIGNhcHRpb24gKGxhYmVsICsgdmFsdWUgKyB0cmFpdHMpIGluCiAgICAgICAgVm9pY2VPdmVyIHJlYWRpbmcgb3JkZXIgcGx1cyBhIHN0YWJsZSBlbGVtZW50IGlkLCBhbmQg4oCUIHdoZXJlIHRoZQogICAgICAgIGFjY2Vzc2liaWxpdHkgYXVkaXQgcmVwb3J0cyBvbmUg4oCUIGl0cyBvbi1zY3JlZW4gcmVjdCBpbiBwb2ludHMuIFBlci1lbGVtZW50CiAgICAgICAgZ2VvbWV0cnkgaXNuJ3QgZXhwb3NlZCBvbiBoYXJkd2FyZSwgc28gb25seSBhdWRpdGVkIGVsZW1lbnRzIGNhcnJ5IGEgcmVjdDsKICAgICAgICB0aGUgVFMgbGF5ZXIgZmlsbHMgdGhlIGdhcHMuIFNjcmVlbiBzaXplIChwb2ludHMpIGNvbWVzIGZyb20gU3ByaW5nQm9hcmQuCiAgICAgICAgIiIiCiAgICAgICAgbGltaXQgPSBpbnQobXNnLmdldCgibGltaXQiLCAxMjApKQoKICAgICAgICAjIDEpIFdhbGsgdGhlIGVsZW1lbnRzIChjYXB0aW9uICsgcmVhZGluZyBvcmRlciArIGVsZW1lbnQgaWQpLgogICAgICAgIHdhbGsgPSBBY2Nlc3NpYmlsaXR5QXVkaXQoc2VsZi5yc2QpCiAgICAgICAgZWxlbWVudHMgPSBbXQogICAgICAgIHRyeToKICAgICAgICAgICAgYXN5bmMgZm9yIGVsIGluIHdhbGsuaXRlcl9lbGVtZW50cygpOgogICAgICAgICAgICAgICAgZWxlbWVudHMuYXBwZW5kKAogICAgICAgICAgICAgICAgICAgIHsiY2FwdGlvbiI6IGVsLmNhcHRpb24sICJpZCI6IGVsLmVsZW1lbnQuaWRlbnRpZmllci5oZXgoKX0KICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIGlmIGxlbihlbGVtZW50cykgPj0gbGltaXQ6CiAgICAgICAgICAgICAgICAgICAgYnJlYWsKICAgICAgICBmaW5hbGx5OgogICAgICAgICAgICB3aXRoIGNvbnRleHRsaWIuc3VwcHJlc3MoRXhjZXB0aW9uKToKICAgICAgICAgICAgICAgIGF3YWl0IHdhbGsuY2xvc2UoKQoKICAgICAgICAjIDIpIEF1ZGl0IGZvciByZWN0cywga2V5ZWQgYnkgZWxlbWVudCBpZCAodGhlIG9ubHkgaGFyZHdhcmUgZnJhbWUgc291cmNlKS4KICAgICAgICByZWN0cyA9IHt9CiAgICAgICAgYXVkaXQgPSBBY2Nlc3NpYmlsaXR5QXVkaXQoc2VsZi5yc2QpCiAgICAgICAgdHJ5OgogICAgICAgICAgICB0eXBlcyA9IGF3YWl0IGF1ZGl0LnN1cHBvcnRlZF9hdWRpdHNfdHlwZXMoKQogICAgICAgICAgICBhd2FpdCBhdWRpdC5fZW5zdXJlX3JlYWR5KCkKICAgICAgICAgICAgYXdhaXQgYXVkaXQuX2ludm9rZSgiZGV2aWNlQmVnaW5BdWRpdFR5cGVzOiIsIHR5cGVzLCBleHBlY3RzX3JlcGx5PUZhbHNlKQogICAgICAgICAgICB3aGlsZSBUcnVlOgogICAgICAgICAgICAgICAgbmFtZSwgYXJncyA9IGF3YWl0IGF1ZGl0Ll9ldmVudF9xdWV1ZS5nZXQoKQogICAgICAgICAgICAgICAgaWYgbmFtZSAhPSAiaG9zdERldmljZURpZENvbXBsZXRlQXVkaXRDYXRlZ29yaWVzV2l0aEF1ZGl0SXNzdWVzOiI6CiAgICAgICAgICAgICAgICAgICAgY29udGludWUKICAgICAgICAgICAgICAgIGlzc3VlcyA9IGRlc2VyaWFsaXplX29iamVjdChhdWRpdC5fZXh0cmFjdF9ldmVudF9wYXlsb2FkKGFyZ3MpKQogICAgICAgICAgICAgICAgIyBpT1MgMjcgcmV0dXJucyB0aGUgQVhBdWRpdElzc3VlIGxpc3QgZGlyZWN0bHk7IG9sZGVyIHdyYXAgaXQgaW4gW3sidmFsdWUiOuKApn1dLgogICAgICAgICAgICAgICAgaWYgKAogICAgICAgICAgICAgICAgICAgIGlzaW5zdGFuY2UoaXNzdWVzLCBsaXN0KQogICAgICAgICAgICAgICAgICAgIGFuZCBpc3N1ZXMKICAgICAgICAgICAgICAgICAgICBhbmQgaXNpbnN0YW5jZShpc3N1ZXNbMF0sIGRpY3QpCiAgICAgICAgICAgICAgICAgICAgYW5kICJ2YWx1ZSIgaW4gaXNzdWVzWzBdCiAgICAgICAgICAgICAgICApOgogICAgICAgICAgICAgICAgICAgIGlzc3VlcyA9IGlzc3Vlc1swXVsidmFsdWUiXQogICAgICAgICAgICAgICAgZm9yIGlzcyBpbiBpc3N1ZXMgb3IgW106CiAgICAgICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgICAgICBlaWQgPSBpc3MuX2ZpZWxkc1siQXVkaXRFbGVtZW50VmFsdWVfdjEiXS5fZmllbGRzWwogICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBsYXRmb3JtRWxlbWVudFZhbHVlX3YxIgogICAgICAgICAgICAgICAgICAgICAgICBdLmhleCgpCiAgICAgICAgICAgICAgICAgICAgICAgIHJlY3QgPSBpc3MuX2ZpZWxkcy5nZXQoIkVsZW1lbnRSZWN0VmFsdWVfdjEiKQogICAgICAgICAgICAgICAgICAgICAgICBpZiBlaWQgYW5kIHJlY3QgYW5kIGVpZCBub3QgaW4gcmVjdHM6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWN0c1tlaWRdID0gcmVjdAogICAgICAgICAgICAgICAgICAgIGV4Y2VwdCBFeGNlcHRpb246CiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgICAgICBicmVhawogICAgICAgIGV4Y2VwdCBFeGNlcHRpb246CiAgICAgICAgICAgIHBhc3MgICMgYXVkaXQgaXMgYmVzdC1lZmZvcnQ7IHRoZSB0cmVlIHN0aWxsIHJldHVybnMgd2l0aG91dCByZWN0cwogICAgICAgIGZpbmFsbHk6CiAgICAgICAgICAgIHdpdGggY29udGV4dGxpYi5zdXBwcmVzcyhFeGNlcHRpb24pOgogICAgICAgICAgICAgICAgYXdhaXQgYXVkaXQuY2xvc2UoKQoKICAgICAgICBmb3IgZWwgaW4gZWxlbWVudHM6CiAgICAgICAgICAgIGlmIGVsWyJpZCJdIGluIHJlY3RzOgogICAgICAgICAgICAgICAgZWxbInJlY3QiXSA9IHJlY3RzW2VsWyJpZCJdXQoKICAgICAgICAjIDMpIFNjcmVlbiBzaXplIGluIHBvaW50cywgZm9yIG5vcm1hbGl6aW5nIHRoZSByZWN0cy4KICAgICAgICBzY3JlZW4gPSBOb25lCiAgICAgICAgd2l0aCBjb250ZXh0bGliLnN1cHByZXNzKEV4Y2VwdGlvbik6CiAgICAgICAgICAgIHNiID0gU3ByaW5nQm9hcmRTZXJ2aWNlc1NlcnZpY2UobG9ja2Rvd249c2VsZi5yc2QpCiAgICAgICAgICAgIG0gPSBhd2FpdCBfbWF5YmVfYXdhaXQoc2IuZ2V0X2hvbWVzY3JlZW5faWNvbl9tZXRyaWNzKCkpCiAgICAgICAgICAgIHNjcmVlbiA9IHsidyI6IG0uZ2V0KCJob21lU2NyZWVuV2lkdGgiKSwgImgiOiBtLmdldCgiaG9tZVNjcmVlbkhlaWdodCIpfQoKICAgICAgICByZXR1cm4geyJlbGVtZW50cyI6IGVsZW1lbnRzLCAic2NyZWVuIjogc2NyZWVufQoKICAgIGFzeW5jIGRlZiBvcF9waW5nKHNlbGYsIF8pOgogICAgICAgIHJldHVybiB7InBvbmciOiBUcnVlfQoKICAgIGFzeW5jIGRlZiBkaXNwYXRjaChzZWxmLCBtc2cpOgogICAgICAgIG9wID0gbXNnLmdldCgib3AiKQogICAgICAgIGZuID0gZ2V0YXR0cihzZWxmLCBmIm9wX3tvcH0iLCBOb25lKQogICAgICAgIGlmIGZuIGlzIE5vbmU6CiAgICAgICAgICAgIHJhaXNlIFJ1bnRpbWVFcnJvcihmInVua25vd24gb3AgJ3tvcH0nIikKICAgICAgICByZXR1cm4gYXdhaXQgZm4obXNnKQoKICAgIGFzeW5jIGRlZiBjbG9zZShzZWxmKToKICAgICAgICB3aXRoIGNvbnRleHRsaWIuc3VwcHJlc3MoRXhjZXB0aW9uKToKICAgICAgICAgICAgYXdhaXQgc2VsZi5zdGFjay5hY2xvc2UoKQogICAgICAgIGlmIHNlbGYucnNkIGlzIG5vdCBOb25lOgogICAgICAgICAgICB3aXRoIGNvbnRleHRsaWIuc3VwcHJlc3MoRXhjZXB0aW9uKToKICAgICAgICAgICAgICAgIGF3YWl0IHNlbGYucnNkLmNsb3NlKCkKCgpkZWYgX2lzXzkwMjEodGV4dDogc3RyKSAtPiBib29sOgogICAgaW1wb3J0IHJlCiAgICByZXR1cm4gYm9vbChyZS5zZWFyY2gociJjb3JlXHMqZGV2aWNlXHMqZXJyb3JcVyo5MDIxIiwgdGV4dCwgcmUuSSkgb3IgcmUuc2VhcmNoKHIiXGI5MDIxXGIiLCB0ZXh0KSkKCgphc3luYyBkZWYgbWFpbigpOgogICAgdWRpZCA9IHN5cy5hcmd2WzFdCiAgICBwb3J0ID0gaW50KHN5cy5hcmd2WzJdKSBpZiBsZW4oc3lzLmFyZ3YpID4gMiBlbHNlIDQ5MTUxCiAgICBhZ2VudCA9IEFnZW50KHVkaWQsIHBvcnQpCiAgICBvdXQgPSBzeXMuc3Rkb3V0CgogICAgZGVmIGVtaXQob2JqKToKICAgICAgICAjIGRlZmF1bHQ9c3RyIGtlZXBzIGEgc3RyYXkgcGxpc3QgdHlwZSAoYnl0ZXMvZGF0ZXRpbWUgaW4gc3ByaW5nYm9hcmQKICAgICAgICAjIGljb24gc3RhdGUpIGZyb20gY3Jhc2hpbmcgc2VyaWFsaXphdGlvbiBtaWQtc2Vzc2lvbi4KICAgICAgICBvdXQud3JpdGUoanNvbi5kdW1wcyhvYmosIGRlZmF1bHQ9c3RyKSArICJcbiIpCiAgICAgICAgb3V0LmZsdXNoKCkKCiAgICB0cnk6CiAgICAgICAgYXdhaXQgYWdlbnQuY29ubmVjdCgpCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6ICAjIG5vcWE6IEJMRTAwMQogICAgICAgIGVtaXQoeyJyZWFkeSI6IEZhbHNlLCAiZXJyb3IiOiBzdHIoZSl9KQogICAgICAgIHJldHVybgogICAgZW1pdCh7InJlYWR5IjogVHJ1ZX0pCgogICAgbG9vcCA9IGFzeW5jaW8uZ2V0X2V2ZW50X2xvb3AoKQogICAgcmVhZGVyID0gYXN5bmNpby5TdHJlYW1SZWFkZXIoKQogICAgYXdhaXQgbG9vcC5jb25uZWN0X3JlYWRfcGlwZShsYW1iZGE6IGFzeW5jaW8uU3RyZWFtUmVhZGVyUHJvdG9jb2wocmVhZGVyKSwgc3lzLnN0ZGluKQoKICAgIHdoaWxlIFRydWU6CiAgICAgICAgbGluZSA9IGF3YWl0IHJlYWRlci5yZWFkbGluZSgpCiAgICAgICAgaWYgbm90IGxpbmU6CiAgICAgICAgICAgIGJyZWFrCiAgICAgICAgbGluZSA9IGxpbmUuc3RyaXAoKQogICAgICAgIGlmIG5vdCBsaW5lOgogICAgICAgICAgICBjb250aW51ZQogICAgICAgIHRyeToKICAgICAgICAgICAgbXNnID0ganNvbi5sb2FkcyhsaW5lKQogICAgICAgIGV4Y2VwdCBFeGNlcHRpb246ICAjIG5vcWE6IEJMRTAwMQogICAgICAgICAgICBlbWl0KHsiZXJyb3IiOiAiYmFkIGpzb24ifSkKICAgICAgICAgICAgY29udGludWUKICAgICAgICBtaWQgPSBtc2cuZ2V0KCJpZCIpCiAgICAgICAgdHJ5OgogICAgICAgICAgICByZXN1bHQgPSBhd2FpdCBhZ2VudC5kaXNwYXRjaChtc2cpCiAgICAgICAgICAgIGVtaXQoeyJpZCI6IG1pZCwgIm9rIjogVHJ1ZSwgKipyZXN1bHR9KQogICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZTogICMgbm9xYTogQkxFMDAxCiAgICAgICAgICAgIHRleHQgPSBmInt0eXBlKGUpLl9fbmFtZV9ffToge2V9IgogICAgICAgICAgICBlbWl0KHsiaWQiOiBtaWQsICJlcnJvciI6IHRleHQsICJnYXRlZF85MDIxIjogX2lzXzkwMjEodGV4dCl9KQoKICAgIGF3YWl0IGFnZW50LmNsb3NlKCkKCgppZiBfX25hbWVfXyA9PSAiX19tYWluX18iOgogICAgYXN5bmNpby5ydW4obWFpbigpKQo="; + +/** Decode the embedded agent program to its Python source. */ +export function coreDeviceAgentScript(): string { + return Buffer.from(AGENT_SCRIPT_B64, "base64").toString("utf8"); +} + +/** + * Materialize the agent program to a stable temp path (content-hashed, so a new + * build's script lands at a new path and an old one is never re-run). Written + * once; concurrent callers race harmlessly on an identical payload. + */ +export async function materializeAgentScript(): Promise { + const script = coreDeviceAgentScript(); + const hash = createHash("sha256").update(script).digest("hex").slice(0, 16); + const path = join(tmpdir(), `argent-coredevice-agent-${hash}.py`); + if (!existsSync(path)) { + await writeFile(path, script, { mode: 0o600 }); + } + return path; +} + +/** + * Resolve the Python interpreter that has pymobiledevice3 importable. The + * `pymobiledevice3` CLI is a thin launcher whose shebang points at its venv + * interpreter (pipx/venv installs), so read the shebang. Falls back to a + * sibling `python`/`python3` in the same bin dir. + */ +export async function resolvePmd3Python(pmd3CliPath: string): Promise { + try { + const head = (await readFile(pmd3CliPath, "utf8")).slice(0, 256); + const firstLine = head.split("\n", 1)[0] ?? ""; + if (firstLine.startsWith("#!")) { + const interp = firstLine.slice(2).trim().split(/\s+/)[0]; + if (interp && interp.startsWith("/") && existsSync(interp)) return interp; + } + } catch { + // not a readable script (e.g. a compiled shim) — fall through to siblings + } + const binDir = dirname(pmd3CliPath); + for (const name of ["python3", "python"]) { + const p = join(binDir, name); + if (existsSync(p)) return p; + } + throw new Error( + `could not resolve the pymobiledevice3 Python interpreter from "${pmd3CliPath}" ` + + `(its shebang and sibling python were both unusable)` + ); +} + +/** A response line from the agent, keyed by the request `id` we sent. */ +interface AgentResponse { + id?: number; + ok?: boolean; + error?: string; + gated_9021?: boolean; + [k: string]: unknown; +} + +/** Raised when the agent replies with an `error` (carries the 9021 hint). */ +export class CoreDeviceAgentError extends Error { + readonly gated9021: boolean; + constructor(message: string, gated9021: boolean) { + super(message); + this.name = "CoreDeviceAgentError"; + this.gated9021 = gated9021; + } +} + +/** + * One long-lived pymobiledevice3 process per physical iPhone. Connects the RSD + * tunnel and opens the HID/screenshot services once, then serves JSON commands + * on stdio — so each tap/screenshot/button costs a socket write, not a fresh + * ~0.8s Python cold-start (of which ~0.5s is just `import pymobiledevice3`). + * + * Requests are id-correlated; the caller may have several in flight, though the + * device serializes them anyway. + */ +export class CoreDeviceAgent { + private proc: ChildProcessWithoutNullStreams | null = null; + private rl: Interface | null = null; + private nextId = 1; + private readonly pending = new Map< + number, + { resolve: (r: AgentResponse) => void; reject: (e: Error) => void } + >(); + private startError: Error | null = null; + private exited = false; + + constructor( + private readonly python: string, + private readonly scriptPath: string, + private readonly udid: string, + private readonly tunneldPort: number, + private readonly startTimeoutMs = 30_000 + ) {} + + /** Spawn the agent and wait for its `{"ready":true}` handshake line. */ + async start(): Promise { + const proc = spawn(this.python, [this.scriptPath, this.udid, String(this.tunneldPort)], { + stdio: ["pipe", "pipe", "pipe"], + }); + this.proc = proc; + + let stderr = ""; + proc.stderr.on("data", (d: Buffer) => { + // pymobiledevice3 logs to stderr; keep a bounded tail for diagnostics. + stderr = (stderr + d.toString()).slice(-4000); + }); + + const rl = createInterface({ input: proc.stdout }); + this.rl = rl; + + const ready = new Promise((resolve, reject) => { + // `onLine` and `timer` reference each other; `timer` is only read from + // inside `onLine`, which never runs before `timer` is assigned below. + const onLine = (line: string): void => { + const trimmed = line.trim(); + if (!trimmed) return; + let msg: AgentResponse; + try { + msg = JSON.parse(trimmed) as AgentResponse; + } catch { + return; // ignore any non-JSON noise before the handshake + } + if ("ready" in msg) { + clearTimeout(timer); + rl.off("line", onLine); + if (msg.ready) { + rl.on("line", (l) => this.onResponse(l)); + resolve(); + } else { + reject(new Error(String(msg.error ?? "agent failed to connect"))); + } + } + }; + const timer = setTimeout(() => { + rl.off("line", onLine); + reject( + new Error( + `CoreDevice agent did not become ready within ${this.startTimeoutMs}ms` + + (stderr ? `; last stderr: ${stderr.slice(-300)}` : "") + ) + ); + }, this.startTimeoutMs); + rl.on("line", onLine); + }); + + proc.on("exit", (code, signal) => { + this.exited = true; + const err = new Error( + `CoreDevice agent exited (code=${code}, signal=${signal})` + + (stderr ? `; stderr: ${stderr.slice(-300)}` : "") + ); + this.startError ??= err; + for (const { reject } of this.pending.values()) reject(err); + this.pending.clear(); + }); + + try { + await ready; + } catch (err) { + this.dispose(); + throw err instanceof Error ? err : new Error(String(err)); + } + } + + private onResponse(line: string): void { + const trimmed = line.trim(); + if (!trimmed) return; + let msg: AgentResponse; + try { + msg = JSON.parse(trimmed) as AgentResponse; + } catch { + return; + } + if (typeof msg.id !== "number") return; + const waiter = this.pending.get(msg.id); + if (!waiter) return; + this.pending.delete(msg.id); + if (msg.error) { + waiter.reject(new CoreDeviceAgentError(String(msg.error), Boolean(msg.gated_9021))); + } else { + waiter.resolve(msg); + } + } + + /** Send one op and await its correlated response. */ + request( + op: string, + args: Record = {}, + timeoutMs = 30_000 + ): Promise { + if (this.exited || !this.proc) { + return Promise.reject(this.startError ?? new Error("CoreDevice agent is not running")); + } + const id = this.nextId++; + return new Promise((resolve, reject) => { + const timer = setTimeout(() => { + if (this.pending.delete(id)) { + reject(new Error(`CoreDevice ${op} timed out after ${timeoutMs}ms`)); + } + }, timeoutMs); + this.pending.set(id, { + resolve: (r) => { + clearTimeout(timer); + resolve(r); + }, + reject: (e) => { + clearTimeout(timer); + reject(e); + }, + }); + this.proc!.stdin.write(JSON.stringify({ id, op, ...args }) + "\n"); + }); + } + + dispose(): void { + this.exited = true; + this.rl?.close(); + this.rl = null; + if (this.proc) { + this.proc.stdin.end(); + this.proc.kill("SIGTERM"); + this.proc = null; + } + for (const { reject } of this.pending.values()) { + reject(new Error("CoreDevice agent disposed")); + } + this.pending.clear(); + } +} diff --git a/packages/tool-server/src/blueprints/device-log-session.ts b/packages/tool-server/src/blueprints/device-log-session.ts new file mode 100644 index 000000000..9e8679897 --- /dev/null +++ b/packages/tool-server/src/blueprints/device-log-session.ts @@ -0,0 +1,206 @@ +import { createWriteStream, type WriteStream } from "node:fs"; +import { readFile } from "node:fs/promises"; +import { spawn, type ChildProcess } from "node:child_process"; +import { join } from "node:path"; +import { existsSync } from "node:fs"; +import { homedir } from "node:os"; +import { + FAILURE_CODES, + FailureError, + TypedEventEmitter, + type DeviceInfo, + type ServiceBlueprint, + type ServiceEvents, + type ServiceInstance, + type ServiceRef, +} from "@argent/registry"; +import { getDebugDir } from "../utils/react-profiler/debug/dump"; + +export const DEVICE_LOG_SESSION_NAMESPACE = "DeviceLogSession"; + +type DeviceLogSessionFactoryOptions = Record & { device: DeviceInfo }; + +export interface DeviceLogStartOptions { + process?: string; +} + +export interface DeviceLogSessionApi { + device: DeviceInfo; + active: boolean; + outputFile: string | null; + startedAtMs: number | null; + start(options: DeviceLogStartOptions): Promise<{ outputFile: string; startedAtMs: number }>; + stop(): Promise<{ outputFile: string; lineCount: number; durationMs: number }>; +} + +export function deviceLogSessionRef(device: DeviceInfo): ServiceRef { + return { urn: `${DEVICE_LOG_SESSION_NAMESPACE}:${device.id}`, options: { device } }; +} + +function sessionFailure( + message: string, + code: (typeof FAILURE_CODES)[keyof typeof FAILURE_CODES], + stage: string, + cause?: unknown +): FailureError { + return new FailureError( + message, + { + error_code: code, + failure_stage: stage, + failure_area: "tool_server", + error_kind: "subprocess", + }, + cause == null ? undefined : { cause: cause instanceof Error ? cause : new Error(String(cause)) } + ); +} + +async function stopChild(child: ChildProcess | null): Promise { + if (!child || child.exitCode != null || child.signalCode != null) return; + child.kill("SIGINT"); + await Promise.race([ + new Promise((resolve) => child.once("exit", () => resolve())), + new Promise((resolve) => setTimeout(resolve, 2_000)), + ]); + if (child.exitCode == null && child.signalCode == null) child.kill("SIGKILL"); +} + +async function closeStream(stream: WriteStream | null): Promise { + if (!stream || stream.closed) return; + await new Promise((resolve) => stream.end(resolve)); +} + +function pymobiledevice3Path(): string { + if (process.env.ARGENT_PYMOBILEDEVICE3) return process.env.ARGENT_PYMOBILEDEVICE3; + for (const candidate of [ + join(homedir(), ".local", "bin", "pymobiledevice3"), + "/opt/homebrew/bin/pymobiledevice3", + "/usr/local/bin/pymobiledevice3", + ]) { + if (existsSync(candidate)) return candidate; + } + return "pymobiledevice3"; +} + +export const deviceLogSessionBlueprint: ServiceBlueprint = { + namespace: DEVICE_LOG_SESSION_NAMESPACE, + getURN(device) { + return `${DEVICE_LOG_SESSION_NAMESPACE}:${device.id}`; + }, + async factory(_deps, _payload, options) { + const opts = options as DeviceLogSessionFactoryOptions | undefined; + if (!opts?.device) { + throw sessionFailure( + `${DEVICE_LOG_SESSION_NAMESPACE}.factory requires options.device.`, + FAILURE_CODES.DEVICE_LOG_SESSION_FACTORY_OPTIONS_MISSING, + "device_log_session_factory" + ); + } + const device = opts.device; + let child: ChildProcess | null = null; + let stream: WriteStream | null = null; + let processFilter: string | undefined; + let lineCount = 0; + + const api: DeviceLogSessionApi = { + device, + active: false, + outputFile: null, + startedAtMs: null, + async start(startOptions) { + if (api.active) { + throw sessionFailure( + `Device log capture is already active for ${device.id}.`, + FAILURE_CODES.DEVICE_LOG_SESSION_ALREADY_ACTIVE, + "device_log_start_state" + ); + } + const debugDir = await getDebugDir(); + const stamp = new Date().toISOString().replace(/[:.]/g, "-"); + api.outputFile = join(debugDir, `device-logs-${device.id}-${stamp}.log`); + api.startedAtMs = Date.now(); + processFilter = startOptions.process; + lineCount = 0; + + try { + stream = createWriteStream(api.outputFile, { flags: "w" }); + if (device.kind === "device") { + const args = ["syslog", "live", "--udid", device.id]; + if (processFilter) args.push("--process-name", processFilter); + child = spawn(pymobiledevice3Path(), args, { stdio: ["ignore", "pipe", "pipe"] }); + } else { + const args = ["simctl", "spawn", device.id, "log", "stream", "--style", "compact"]; + if (processFilter) args.push("--predicate", `process == ${JSON.stringify(processFilter)}`); + child = spawn("xcrun", args, { stdio: ["ignore", "pipe", "pipe"] }); + } + const count = (chunk: Buffer) => { + lineCount += chunk.toString("utf8").split("\n").length - 1; + stream?.write(chunk); + }; + child.stdout?.on("data", count); + child.stderr?.on("data", count); + await new Promise((resolve, reject) => { + const timer = setTimeout(resolve, 350); + child!.once("error", (error) => { + clearTimeout(timer); + reject(error); + }); + child!.once("exit", (code) => { + if (code != null && code !== 0) { + clearTimeout(timer); + reject(new Error(`log stream exited with code ${code}`)); + } + }); + }); + } catch (error) { + await stopChild(child); + await closeStream(stream); + child = null; + stream = null; + throw sessionFailure( + `Could not start device log capture for ${device.id}.`, + FAILURE_CODES.DEVICE_LOG_SESSION_START_FAILED, + "device_log_start", + error + ); + } + + api.active = true; + return { outputFile: api.outputFile, startedAtMs: api.startedAtMs }; + }, + async stop() { + if (!api.active || !api.outputFile || api.startedAtMs == null) { + throw sessionFailure( + `No active device log capture exists for ${device.id}.`, + FAILURE_CODES.DEVICE_LOG_SESSION_NOT_ACTIVE, + "device_log_stop_state" + ); + } + const outputFile = api.outputFile; + const startedAtMs = api.startedAtMs; + await stopChild(child); + await closeStream(stream); + // Include a final count from disk if the stream delivered a partial + // last line without a trailing newline. + const text = await readFile(outputFile, "utf8").catch(() => ""); + lineCount = text.length === 0 ? 0 : text.split("\n").filter(Boolean).length; + child = null; + stream = null; + api.active = false; + return { outputFile, lineCount, durationMs: Date.now() - startedAtMs }; + }, + }; + + const events = new TypedEventEmitter(); + const instance: ServiceInstance = { + api, + events, + async dispose() { + await stopChild(child); + await closeStream(stream); + api.active = false; + }, + }; + return instance; + }, +}; diff --git a/packages/tool-server/src/blueprints/native-devtools.ts b/packages/tool-server/src/blueprints/native-devtools.ts index 72e0bee94..3150b7db7 100644 --- a/packages/tool-server/src/blueprints/native-devtools.ts +++ b/packages/tool-server/src/blueprints/native-devtools.ts @@ -311,6 +311,20 @@ export const nativeDevtoolsBlueprint: ServiceBlueprint & { device: DeviceInfo }; + +export interface PhysicalIosTouchEvent { + type: "Down" | "Move" | "Up"; + x: number; + y: number; + x2?: number; + y2?: number; + delayMs?: number; +} + +export interface PhysicalIosLogEntry { + timestamp: number; + level: string; + message: string; +} + +export interface PhysicalIosAutomationApi { + screenshot(): Promise<{ path: string }>; + source(): Promise; + windowSize(): Promise<{ width: number; height: number }>; + tap(x: number, y: number): Promise; + swipe(fromX: number, fromY: number, toX: number, toY: number, durationMs: number): Promise; + touch(events: PhysicalIosTouchEvent[]): Promise; + button(name: "home" | "power" | "volumeUp" | "volumeDown" | "actionButton"): Promise; + typeText(text: string, delayMs?: number): Promise; + pasteText(text: string): Promise; + pressKey(key: string): Promise; + rotate(orientation: "Portrait" | "LandscapeLeft" | "LandscapeRight" | "PortraitUpsideDown"): Promise; + launchApp(bundleId: string): Promise; + openUrl(url: string, bundleId?: string): Promise; + terminateApp(bundleId: string): Promise; + activeApp(): Promise<{ bundleId: string; pid: number; name?: string }>; + drainLogs(): Promise; + /** Wait until every previously registered control has completed on-device. */ + flushControls(): Promise; +} + +export function physicalIosAutomationRef(device: DeviceInfo): { + urn: string; + options: PhysicalIosAutomationFactoryOptions; +} { + return { + urn: `${PHYSICAL_IOS_AUTOMATION_NAMESPACE}:${device.id}`, + options: { device }, + }; +} + +interface DeviceDetails { + name: string; + osVersion: string; +} + +interface WdaBuild { + bundleId: string; + derivedDataPath: string; + teamId: string; +} + +function clampUnit(value: number): number { + return Math.max(0, Math.min(1, value)); +} + +// The simulator-server sends one IPC message per touch point. Across idle and +// concurrently-profiled runs this contributes roughly 0.7–1.8 ms per point on +// top of the 16 ms frame cadence. The midpoint keeps physical registration +// within 5% in both conditions while XCTest work continues in-order behind the +// queue. +const SIMULATOR_IPC_POINT_MS = 1.2; + +function simulatorGestureCadenceMs(durationMs: number): number { + const steps = Math.max(1, Math.round(durationMs / 16)); + return steps * 16 + (steps + 1) * SIMULATOR_IPC_POINT_MS; +} + +function safeSegment(value: string): string { + return value.replace(/[^A-Za-z0-9._-]/g, "_"); +} + +function failure( + message: string, + code: (typeof FAILURE_CODES)[keyof typeof FAILURE_CODES], + stage: string, + cause?: unknown +): FailureError { + return new FailureError( + message, + { + error_code: code, + failure_stage: stage, + failure_area: "tool_server", + error_kind: "subprocess", + }, + cause == null ? undefined : { cause: cause instanceof Error ? cause : new Error(String(cause)) } + ); +} + +async function physicalDeviceDetails(udid: string): Promise { + const outputPath = join(tmpdir(), `argent-ios-details-${randomUUID()}.json`); + try { + await execFileAsync( + "xcrun", + [ + "devicectl", + "device", + "info", + "details", + "--device", + udid, + "--quiet", + "--json-output", + outputPath, + ], + { timeout: 30_000 } + ); + const parsed = JSON.parse(await readFile(outputPath, "utf8")) as { + result?: { + deviceProperties?: { name?: string; osVersionNumber?: string }; + }; + }; + const props = parsed.result?.deviceProperties; + if (!props?.osVersionNumber) throw new Error("devicectl omitted the iOS version"); + return { name: props.name ?? "iPhone", osVersion: props.osVersionNumber }; + } catch (error) { + throw failure( + `Could not read details for physical iOS device ${udid}. Ensure it is connected, unlocked, trusted, and has Developer Mode enabled.`, + FAILURE_CODES.PHYSICAL_IOS_AUTOMATION_DEVICE_DETAILS_FAILED, + "physical_ios_device_details", + error + ); + } finally { + await rm(outputPath, { force: true }).catch(() => {}); + } +} + +async function discoverSigningTeams(): Promise { + if (process.env.ARGENT_WDA_TEAM_ID) return [process.env.ARGENT_WDA_TEAM_ID]; + try { + const { stdout } = await execFileAsync( + "defaults", + ["read", "com.apple.dt.Xcode", "IDEProvisioningTeamByIdentifier"], + { timeout: 10_000 } + ); + const teams = [...stdout.matchAll(/teamID\s*=\s*([A-Z0-9]+);/g)].map((match) => match[1]!); + return [...new Set(teams)]; + } catch { + return []; + } +} + +function webdriverAgentPaths(): { projectPath: string; version: string } { + const driverPackagePath = require.resolve("appium-xcuitest-driver/package.json"); + const driverDir = dirname(driverPackagePath); + const wdaPackagePath = require.resolve("appium-webdriveragent/package.json", { + paths: [driverDir], + }); + const wdaPackage = require(wdaPackagePath) as { version?: string }; + return { + projectPath: join(dirname(wdaPackagePath), "WebDriverAgent.xcodeproj"), + version: wdaPackage.version ?? "unknown", + }; +} + +function appPathForDerivedData(derivedDataPath: string): string { + return join( + derivedDataPath, + "Build", + "Products", + "Debug-iphoneos", + "WebDriverAgentRunner-Runner.app" + ); +} + +async function verifySignedApp(appPath: string): Promise { + if (!existsSync(appPath)) return false; + try { + await execFileAsync("codesign", ["--verify", "--deep", "--strict", appPath], { + timeout: 30_000, + }); + return true; + } catch { + return false; + } +} + +async function signingIdentityForTeam(teamId: string): Promise { + if (process.env.ARGENT_WDA_SIGNING_ID) return process.env.ARGENT_WDA_SIGNING_ID; + const { stdout } = await execFileAsync( + "security", + ["find-certificate", "-a", "-Z", join(homedir(), "Library/Keychains/login.keychain-db")], + { timeout: 30_000, maxBuffer: 16 * 1024 * 1024 } + ); + const matches: string[] = []; + for (const block of stdout.split(/(?=SHA-256 hash:)/)) { + if (!block.includes(teamId) || !block.includes("Apple Development:")) continue; + const sha1 = /^SHA-1 hash:\s*([A-F0-9]{40})$/m.exec(block)?.[1]; + if (sha1) matches.push(sha1); + } + // Keychain may contain two certificates with the same display name. The + // most recently returned matching identity is the one Xcode currently uses; + // passing its SHA-1 avoids both the name ambiguity and a stale private-key + // access prompt from an older duplicate. + if (matches.length > 0) return matches[matches.length - 1]!; + throw new Error( + `no Apple Development certificate with a private key was found for team ${teamId}` + ); +} + +async function buildAndResignWda( + udid: string, + osVersion: string, + teamId: string, + bundleId: string, + derivedDataPath: string, + projectPath: string +): Promise { + await mkdir(derivedDataPath, { recursive: true }); + const identity = await signingIdentityForTeam(teamId); + await execFileAsync( + "xcodebuild", + [ + "build-for-testing", + "-allowProvisioningUpdates", + "-allowProvisioningDeviceRegistration", + "-project", + projectPath, + "-scheme", + "WebDriverAgentRunner", + "-destination", + `id=${udid}`, + "-derivedDataPath", + derivedDataPath, + `IPHONEOS_DEPLOYMENT_TARGET=${osVersion}`, + `DEVELOPMENT_TEAM=${teamId}`, + "CODE_SIGN_IDENTITY=Apple Development", + `EXPANDED_CODE_SIGN_IDENTITY=${identity}`, + `PRODUCT_BUNDLE_IDENTIFIER=${bundleId}`, + "GCC_TREAT_WARNINGS_AS_ERRORS=0", + "COMPILER_INDEX_STORE_ENABLE=NO", + ], + { timeout: 10 * 60_000, maxBuffer: 64 * 1024 * 1024 } + ); + + const appPath = appPathForDerivedData(derivedDataPath); + if (!existsSync(appPath)) throw new Error(`xcodebuild did not produce ${appPath}`); + + // Xcode 26 can leave the generated XCTest runner wrapper with an invalid + // top-level signature even though its nested .xctest is valid. Physical iOS + // rejects that wrapper with 0xe8008001. Removing provenance xattrs and + // re-signing the complete runner with the identity Xcode selected makes the + // result installable while preserving its identifiers and entitlements. + await execFileAsync("xattr", ["-cr", appPath], { timeout: 30_000 }); + await execFileAsync( + "codesign", + [ + "--force", + "--deep", + "--sign", + identity, + "--preserve-metadata=identifier,entitlements,requirements,flags,runtime", + appPath, + ], + { timeout: 60_000, maxBuffer: 8 * 1024 * 1024 } + ); + await execFileAsync("codesign", ["--verify", "--deep", "--strict", "--verbose=2", appPath], { + timeout: 30_000, + }); +} + +async function prepareWda(udid: string, details: DeviceDetails): Promise { + const teams = await discoverSigningTeams(); + if (teams.length === 0) { + throw failure( + "No Apple development team is configured in Xcode. Add an Apple Account in Xcode Settings → Accounts, or set ARGENT_WDA_TEAM_ID.", + FAILURE_CODES.PHYSICAL_IOS_AUTOMATION_SIGNING_TEAM_NOT_FOUND, + "physical_ios_wda_signing_team" + ); + } + + const { projectPath, version } = webdriverAgentPaths(); + const configuredBundleId = process.env.ARGENT_WDA_BUNDLE_ID; + const failures: string[] = []; + + for (const teamId of teams) { + const bundleId = configuredBundleId ?? `dev.argent.WebDriverAgentRunner.${teamId.toLowerCase()}`; + const cacheKey = createHash("sha256") + .update(`${version}:${teamId}:${bundleId}:${details.osVersion}`) + .digest("hex") + .slice(0, 16); + const derivedDataPath = join( + homedir(), + ".argent", + "webdriveragent", + safeSegment(version), + cacheKey + ); + const appPath = appPathForDerivedData(derivedDataPath); + + if (await verifySignedApp(appPath)) return { bundleId, derivedDataPath, teamId }; + + try { + await buildAndResignWda( + udid, + details.osVersion, + teamId, + bundleId, + derivedDataPath, + projectPath + ); + return { bundleId, derivedDataPath, teamId }; + } catch (error) { + const stderr = (error as { stderr?: string }).stderr ?? String(error); + failures.push(`${teamId}: ${stderr.split("\n").slice(-8).join(" ").trim()}`); + if (configuredBundleId || process.env.ARGENT_WDA_TEAM_ID) break; + } + } + + throw failure( + `WebDriverAgent could not be built and signed for ${details.name}. ` + + `Set ARGENT_WDA_TEAM_ID and, if your team requires an explicit App ID, ARGENT_WDA_BUNDLE_ID.\n` + + failures.join("\n"), + FAILURE_CODES.PHYSICAL_IOS_AUTOMATION_WDA_BUILD_FAILED, + "physical_ios_wda_build" + ); +} + +function pointerActions( + id: string, + points: Array<{ x: number; y: number; duration?: number }>, + holdMs = 0 +): Record { + const actions: Array> = [ + { type: "pointerMove", duration: 0, x: points[0]!.x, y: points[0]!.y, origin: "viewport" }, + { type: "pointerDown", button: 0 }, + ]; + if (holdMs > 0) actions.push({ type: "pause", duration: holdMs }); + for (const point of points.slice(1)) { + actions.push({ + type: "pointerMove", + duration: point.duration ?? 0, + x: point.x, + y: point.y, + origin: "viewport", + }); + } + actions.push({ type: "pointerUp", button: 0 }); + return { type: "pointer", id, parameters: { pointerType: "touch" }, actions }; +} + +function customTouchActions( + events: PhysicalIosTouchEvent[], + width: number, + height: number +): Array> { + const hasSecondPointer = events.some((event) => event.x2 != null && event.y2 != null); + const first: Array> = []; + const second: Array> = []; + + const appendBoth = (a: Record, b: Record) => { + first.push(a); + if (hasSecondPointer) second.push(b); + }; + + for (const [index, event] of events.entries()) { + // WDA requires every pointer sequence to start with pointerMove. A delay + // before the first Down is therefore meaningless (the finger is not on the + // screen yet) and invalid in XCTest's action parser; delays before later + // events preserve Argent's gesture-custom semantics. + const duration = index === 0 ? 0 : Math.max(0, event.delayMs ?? 16); + const p1 = { + type: "pointerMove", + duration, + x: clampUnit(event.x) * width, + y: clampUnit(event.y) * height, + origin: "viewport", + }; + const p2 = + event.x2 != null && event.y2 != null + ? { + type: "pointerMove", + duration, + x: clampUnit(event.x2) * width, + y: clampUnit(event.y2) * height, + origin: "viewport", + } + : { type: "pause", duration }; + appendBoth(p1, p2); + + const operation = + event.type === "Down" + ? { type: "pointerDown", button: 0 } + : event.type === "Up" + ? { type: "pointerUp", button: 0 } + : { type: "pause", duration: 0 }; + appendBoth(operation, event.x2 != null && event.y2 != null ? { ...operation } : { type: "pause", duration: 0 }); + } + + const result: Array> = [ + { type: "pointer", id: "finger1", parameters: { pointerType: "touch" }, actions: first }, + ]; + if (hasSecondPointer) { + result.push({ + type: "pointer", + id: "finger2", + parameters: { pointerType: "touch" }, + actions: second, + }); + } + return result; +} + +function pointSegmentDistance( + point: readonly number[], + start: readonly number[], + end: readonly number[] +): number { + let lengthSquared = 0; + let dot = 0; + for (let i = 0; i < point.length; i++) { + const delta = end[i]! - start[i]!; + lengthSquared += delta * delta; + dot += (point[i]! - start[i]!) * delta; + } + const t = lengthSquared === 0 ? 0 : Math.max(0, Math.min(1, dot / lengthSquared)); + let distanceSquared = 0; + for (let i = 0; i < point.length; i++) { + const delta = point[i]! - (start[i]! + (end[i]! - start[i]!) * t); + distanceSquared += delta * delta; + } + return Math.sqrt(distanceSquared); +} + +function touchPoint(event: PhysicalIosTouchEvent, twoFinger: boolean): number[] { + return twoFinger + ? [event.x, event.y, event.x2 ?? event.x, event.y2 ?? event.y] + : [event.x, event.y]; +} + +/** + * XCTest's synthesized-event setup cost grows with every path vertex. Argent's + * simulator backend emits one point per ~16 ms, but XCTest already interpolates + * between timed pointer moves. Remove redundant collinear vertices while + * retaining curves, explicit contact boundaries, and the original total time. + */ +export function compactPhysicalTouchEvents( + events: PhysicalIosTouchEvent[], + epsilon = 0.002 +): PhysicalIosTouchEvent[] { + if ( + events.length <= 2 || + events[0]?.type !== "Down" || + events.at(-1)?.type !== "Up" || + events.slice(1, -1).some((event) => event.type !== "Move") + ) { + return events; + } + + const twoFinger = events.some((event) => event.x2 != null && event.y2 != null); + if ( + twoFinger && + events.some((event) => event.x2 == null || event.y2 == null) + ) { + return events; + } + + const kept = new Set([0, events.length - 1]); + const visit = (startIndex: number, endIndex: number): void => { + const start = touchPoint(events[startIndex]!, twoFinger); + const end = touchPoint(events[endIndex]!, twoFinger); + let maxDistance = epsilon; + let maxIndex = -1; + for (let i = startIndex + 1; i < endIndex; i++) { + const distance = pointSegmentDistance(touchPoint(events[i]!, twoFinger), start, end); + if (distance > maxDistance) { + maxDistance = distance; + maxIndex = i; + } + } + if (maxIndex === -1) return; + kept.add(maxIndex); + visit(startIndex, maxIndex); + visit(maxIndex, endIndex); + }; + visit(0, events.length - 1); + + const indices = [...kept].sort((a, b) => a - b); + return indices.map((index, position) => { + if (position === 0) return { ...events[index]! }; + const previousIndex = indices[position - 1]!; + let delayMs = 0; + for (let i = previousIndex + 1; i <= index; i++) { + delayMs += Math.max(0, events[i]!.delayMs ?? 16); + } + return { ...events[index]!, delayMs }; + }); +} + +const KEY_NAMES: Record = { + enter: "\r", + escape: "XCUIKeyboardKeyEscape", + backspace: "XCUIKeyboardKeyDelete", + tab: "XCUIKeyboardKeyTab", + space: " ", + "arrow-up": "XCUIKeyboardKeyUpArrow", + "arrow-down": "XCUIKeyboardKeyDownArrow", + "arrow-left": "XCUIKeyboardKeyLeftArrow", + "arrow-right": "XCUIKeyboardKeyRightArrow", +}; + +export const physicalIosAutomationBlueprint: ServiceBlueprint< + PhysicalIosAutomationApi, + DeviceInfo +> = { + namespace: PHYSICAL_IOS_AUTOMATION_NAMESPACE, + getURN(device) { + return `${PHYSICAL_IOS_AUTOMATION_NAMESPACE}:${device.id}`; + }, + recoverable(error) { + const message = error instanceof Error ? error.message : String(error); + return /ECONNREFUSED|socket hang up|WebDriverAgent is not reachable/i.test(message); + }, + async factory(_deps, _payload, options) { + const opts = options as PhysicalIosAutomationFactoryOptions | undefined; + const device = opts?.device; + if (!device?.id) { + throw failure( + `${PHYSICAL_IOS_AUTOMATION_NAMESPACE}.factory requires options.device.`, + FAILURE_CODES.PHYSICAL_IOS_AUTOMATION_FACTORY_OPTIONS_MISSING, + "physical_ios_automation_factory_options" + ); + } + if (device.platform !== "ios" || device.kind !== "device") { + throw failure( + `${PHYSICAL_IOS_AUTOMATION_NAMESPACE} only drives physical iOS devices; got ${device.platform}/${device.kind}.`, + FAILURE_CODES.PHYSICAL_IOS_AUTOMATION_WRONG_DEVICE, + "physical_ios_automation_factory_platform" + ); + } + assertPhysicalIosEnabled(); + + const details = await physicalDeviceDetails(device.id); + const wdaBuild = await prepareWda(device.id, details); + const driver = new XCUITestDriver({} as never); + const caps = { + alwaysMatch: { + platformName: "iOS", + "appium:automationName": "XCUITest", + "appium:udid": device.id, + "appium:deviceName": details.name, + "appium:platformVersion": details.osVersion, + "appium:noReset": true, + "appium:useNewWDA": false, + "appium:usePrebuiltWDA": true, + "appium:derivedDataPath": wdaBuild.derivedDataPath, + "appium:updatedWDABundleId": wdaBuild.bundleId, + "appium:xcodeOrgId": wdaBuild.teamId, + "appium:xcodeSigningId": process.env.ARGENT_WDA_SIGNING_ID ?? "Apple Development", + "appium:wdaLaunchTimeout": 240_000, + "appium:wdaConnectionTimeout": 240_000, + "appium:showXcodeLog": process.env.ARGENT_WDA_SHOW_XCODE_LOG === "1", + "appium:waitForIdleTimeout": 0, + "appium:useJSONSource": false, + }, + firstMatch: [{}], + }; + + try { + await (driver.createSession as unknown as (...args: unknown[]) => Promise)( + null, + null, + caps + ); + // WDA otherwise waits up to two seconds for Maps' continuously animating + // render tree to become stable after every action. Input has already been + // synthesized at this point, so this wait only inflates acknowledgement + // latency and is inappropriate for an agent-control transport. + await driver.updateSettings({ animationCoolOffTimeout: 0 }); + } catch (error) { + throw failure( + `WebDriverAgent could not start on ${details.name} (${device.id}). Keep the phone unlocked and trust the developer certificate if iOS prompts for it.`, + FAILURE_CODES.PHYSICAL_IOS_AUTOMATION_WDA_SESSION_FAILED, + "physical_ios_wda_session", + error + ); + } + + let queue: Promise = Promise.resolve(); + let queuedFailure: FailureError | null = null; + const commandFailure = (label: string, error: unknown): FailureError => + error instanceof FailureError + ? error + : failure( + `Physical iOS ${label} failed on ${details.name}: ${error instanceof Error ? error.message : String(error)}`, + FAILURE_CODES.PHYSICAL_IOS_AUTOMATION_COMMAND_FAILED, + `physical_ios_${label}`, + error + ); + + const serialized = async (label: string, operation: () => Promise): Promise => { + const run = queue.then(async () => { + if (queuedFailure) { + const error = queuedFailure; + queuedFailure = null; + throw error; + } + return operation(); + }); + queue = run.then( + () => undefined, + () => undefined + ); + try { + return await run; + } catch (error) { + throw commandFailure(label, error); + } + }; + + /** + * Register a control without waiting for XCTest's response bookkeeping. + * The queue still preserves strict device order. Acknowledgement is paced + * to the gesture's own duration, matching the simulator backend (which + * sleeps while emitting its 60 fps points) to within scheduling jitter. + * Read operations and capture stop calls use flushControls as a barrier and + * surface any deferred WDA error. + */ + const enqueueControl = async ( + label: string, + operation: () => Promise, + acknowledgeDelayMs = 0 + ): Promise => { + if (queuedFailure) { + const error = queuedFailure; + queuedFailure = null; + throw error; + } + const run = queue.then(operation); + queue = run.then( + () => undefined, + (error) => { + queuedFailure = commandFailure(label, error); + } + ); + if (acknowledgeDelayMs > 0) { + await new Promise((resolve) => setTimeout(resolve, acknowledgeDelayMs)); + } + }; + + let cachedDimensions: { width: number; height: number } | null = await driver.getWindowSize(); + const dimensions = async () => { + if (cachedDimensions) return cachedDimensions; + cachedDimensions = await serialized("window_size", () => driver.getWindowSize()); + return cachedDimensions; + }; + const api: PhysicalIosAutomationApi = { + async screenshot() { + return serialized("screenshot", async () => { + const base64 = await driver.getScreenshot(); + const path = join(tmpdir(), `argent-physical-ios-${randomUUID()}.png`); + await writeFile(path, Buffer.from(base64, "base64")); + return { path }; + }); + }, + source: () => serialized("source", () => driver.getPageSource()), + windowSize: dimensions, + async tap(x, y) { + const size = await dimensions(); + await enqueueControl( + "tap", + () => driver.mobileTap(clampUnit(x) * size.width, clampUnit(y) * size.height), + 52 + ); + }, + async swipe(fromX, fromY, toX, toY, durationMs) { + const size = await dimensions(); + await enqueueControl( + "swipe", + () => + driver.performActions([ + pointerActions("finger1", [ + { x: clampUnit(fromX) * size.width, y: clampUnit(fromY) * size.height }, + { + x: clampUnit(toX) * size.width, + y: clampUnit(toY) * size.height, + duration: Math.max(50, durationMs), + }, + ]), + ] as never), + simulatorGestureCadenceMs(durationMs) + ); + }, + async touch(events) { + if (events.length === 0) return; + const size = await dimensions(); + const compacted = compactPhysicalTouchEvents(events); + const durationMs = events.reduce( + (sum, event) => sum + Math.max(0, event.delayMs ?? 16), + 0 + ) + events.length * SIMULATOR_IPC_POINT_MS; + await enqueueControl( + "touch", + () => + driver.performActions(customTouchActions(compacted, size.width, size.height) as never), + durationMs + ); + }, + async button(name) { + await enqueueControl("button", async () => { + if (name === "power") { + await driver.lock(); + return; + } + const mapped = + name === "volumeUp" + ? "volumeup" + : name === "volumeDown" + ? "volumedown" + : name === "actionButton" + ? "action" + : "home"; + await driver.proxyCommand("/wda/pressButton", "POST", { name: mapped }); + }); + }, + async typeText(text, delayMs) { + const simulatorDelayMs = delayMs ?? 50; + await enqueueControl( + "keyboard", + async () => { + if (delayMs && delayMs > 0) { + for (const char of text) { + await driver.keys(char); + await new Promise((resolve) => setTimeout(resolve, delayMs)); + } + } else { + await driver.keys(text); + } + }, + text.length * simulatorDelayMs * 2 + ); + }, + async pressKey(key) { + const mapped = KEY_NAMES[key] ?? (/^f(?:[1-9]|1[0-2])$/i.test(key) ? `XCUIKeyboardKey${key.toUpperCase()}` : undefined); + if (!mapped) throw new Error(`Unsupported physical iOS key: ${key}`); + await enqueueControl("keyboard_key", () => driver.mobileKeys([mapped]), 50); + }, + async pasteText(text) { + await enqueueControl("paste", () => driver.keys(text)); + }, + async rotate(orientation) { + const mapped = orientation.startsWith("Landscape") ? "LANDSCAPE" : "PORTRAIT"; + await enqueueControl("rotate", () => + driver.proxyCommand("/orientation", "POST", { orientation: mapped }) + ); + }, + launchApp: (bundleId) => serialized("launch_app", () => driver.mobileLaunchApp(bundleId)), + openUrl: (url, bundleId) => + enqueueControl("open_url", () => driver.mobileDeepLink(url, bundleId)), + terminateApp: (bundleId) => serialized("terminate_app", () => driver.mobileTerminateApp(bundleId)), + async activeApp() { + return serialized("active_app", async () => { + const app = await driver.mobileGetActiveAppInfo(); + return { bundleId: app.bundleId, pid: app.pid, name: app.name || undefined }; + }); + }, + drainLogs: () => serialized("device_logs", () => driver.extractLogs("syslog")), + flushControls: () => serialized("control_flush", async () => {}), + }; + + const events = new TypedEventEmitter(); + const instance: ServiceInstance = { + api, + events, + async dispose() { + await Promise.race([ + queue, + new Promise((resolve) => setTimeout(resolve, 30_000)), + ]).catch(() => {}); + await driver.wda.xcodebuild.quit().catch(() => {}); + const cleanup = driver.deleteSession().catch(() => {}); + await Promise.race([cleanup, new Promise((resolve) => setTimeout(resolve, 5_000))]); + }, + }; + return instance; + }, +}; diff --git a/packages/tool-server/src/blueprints/simulator-server.ts b/packages/tool-server/src/blueprints/simulator-server.ts index 4f57741eb..458545996 100644 --- a/packages/tool-server/src/blueprints/simulator-server.ts +++ b/packages/tool-server/src/blueprints/simulator-server.ts @@ -332,6 +332,21 @@ export const simulatorServerBlueprint: ServiceBlueprint((d) => { + if (d.platform === "ios") { + if (d.kind === "device") return []; + return [ + { + udid: d.udid, + name: d.name, + state: d.state, + runtime: d.runtime ?? "", + isAvailable: true, + platform: "ios", + }, + ]; + } + if (d.platform === "android") { + return [ + { + udid: d.serial, + name: d.avdName ?? d.model ?? d.serial, + state: d.state === "device" ? "Booted" : d.state, + runtime: d.sdkLevel != null ? `Android API ${d.sdkLevel}` : "Android", + isAvailable: true, + platform: "android", + }, + ]; + } + return []; + }); +} + export function createPreviewRouter(registry: Registry): Router { const router = express.Router(); @@ -93,7 +146,7 @@ export function createPreviewRouter(registry: Registry): Router { // udid, every other platform by its serial (a chromium entry has neither, so // it's skipped — it was never a valid preview target anyway). function deviceIdSet( - devices: ReadonlyArray<{ platform: string; udid?: string; serial?: string }> + devices: ReadonlyArray<{ platform: string; udid?: string; serial?: string | null }> ): Set { const ids = new Set(); for (const d of devices) { @@ -107,7 +160,7 @@ export function createPreviewRouter(registry: Registry): Router { // dedicated refresh below and the /simulators handler, which already fetches // the full list for its dropdown). function rememberDevices( - devices: ReadonlyArray<{ platform: string; udid?: string; serial?: string }> + devices: ReadonlyArray<{ platform: string; udid?: string; serial?: string | null }> ): void { knownDevices = { ids: deviceIdSet(devices), at: Date.now() }; } @@ -137,71 +190,19 @@ export function createPreviewRouter(registry: Registry): Router { router.get("/simulators", async (_req: Request, res: Response) => { try { - const data = await registry.invokeTool<{ - devices: Array< - | { platform: "ios"; udid: string; name: string; state: string; runtime: string } - | { - platform: "android"; - serial: string; - state: string; - avdName?: string; - model?: string; - sdkLevel?: number | null; - } - | { platform: "chromium"; id: string; title: string; port: number } - >; - }>(listDevicesTool.id); - // The preview UI keys off `udid` and `state === "Booted"`, which are - // iOS terminology. Map Android serials to the same shape so the same - // dropdown can target both platforms — `simulator-server/:udid` already - // accepts Android serials via `resolveDevice(udid)`. - // - // Chromium is intentionally excluded: the preview UI streams frames - // through simulator-server's WebSocket, which only exists for iOS / - // Android. Surfacing chromium entries would let the UI offer a target - // it can't actually drive. Chromium consumers should use the MCP tools - // (screenshot, describe, gesture-*) directly. - type PreviewEntry = { - udid: string; - name: string; - state: string; - runtime: string; - isAvailable: boolean; - platform: "ios" | "android"; - }; - const simulators = data.devices.flatMap((d) => { - if (d.platform === "ios") { - return [ - { - udid: d.udid, - name: d.name, - state: d.state, - runtime: d.runtime, - isAvailable: true, - platform: "ios", - }, - ]; - } - if (d.platform === "android") { - return [ - { - udid: d.serial, - name: d.avdName ?? d.model ?? d.serial, - state: d.state === "device" ? "Booted" : d.state, - runtime: d.sdkLevel != null ? `Android API ${d.sdkLevel}` : "Android", - isAvailable: true, - platform: "android", - }, - ]; - } - return []; - }); + // Reuse list-devices' own result type rather than a hand-rolled copy, so + // the preview's view of a device can't silently drift from what the tool + // actually returns (e.g. physical-iOS entries carrying `kind`/no runtime). + const data = await registry.invokeTool(listDevicesTool.id); // This is the authoritative fresh device list — prime the validation // cache so the immediately-following connect (/simulator-server/:udid) // and the describe poll loop hit a warm, correct set instead of each // re-running `list-devices`. rememberDevices(data.devices); - res.json({ simulators }); + // devicesToPreviewEntries maps iOS/Android into the UI's udid/Booted shape + // and excludes targets the preview can't stream — chromium (no + // simulator-server WebSocket) and physical iOS (kind: "device"). + res.json({ simulators: devicesToPreviewEntries(data.devices) }); } catch (err) { res.status(500).json({ error: err instanceof Error ? err.message : String(err) }); } diff --git a/packages/tool-server/src/tools/button/index.ts b/packages/tool-server/src/tools/button/index.ts index 8aa9db025..543112e23 100644 --- a/packages/tool-server/src/tools/button/index.ts +++ b/packages/tool-server/src/tools/button/index.ts @@ -1,12 +1,28 @@ import { z } from "zod"; import type { Platform, ServiceRef, ToolCapability, ToolDefinition } from "@argent/registry"; import { simulatorServerRef, type SimulatorServerApi } from "../../blueprints/simulator-server"; -import { resolveDevice } from "../../utils/device-info"; +import { + physicalIosAutomationRef, + type PhysicalIosAutomationApi, +} from "../../blueprints/physical-ios-automation"; +import { resolveDevice, isPhysicalIos } from "../../utils/device-info"; import { UnsupportedOperationError } from "../../utils/capability"; import { sendCommand } from "../../utils/simulator-client"; import { ANDROID_BUTTON_KEYCODES, injectAndroidKeycode } from "../../utils/android-input"; import { ensureDep } from "../../utils/check-deps"; +// Argent button name → physical WDA action. XCTest exposes the Action button +// on supported phones, but it has no public App Switcher command. +const PHYSICAL_IOS_BUTTON: Partial< + Record +> = { + home: "home", + power: "power", + volumeUp: "volumeUp", + volumeDown: "volumeDown", + actionButton: "actionButton", +}; + const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); const zodSchema = z.object({ @@ -58,6 +74,7 @@ export const buttonTool: ToolDefinition = { Supported buttons depend on the platform: home, back, power, volumeUp, volumeDown, appSwitch, actionButton — buttons not present on the target platform (e.g. 'back' on iOS, 'actionButton' on Android) are rejected with a clear error. Use when you need to trigger hardware button events. Returns { pressed: buttonName }. +On a physical iPhone, Home/Power/volume and Action-button presses route through WebDriverAgent; App Switcher is not exposed by XCTest. Fails if the device backend is not reachable — the simulator-server for iOS, or \`adb\` for Android (Android presses are injected with \`adb shell input keyevent\`).`, zodSchema, capability, @@ -69,6 +86,11 @@ Fails if the device backend is not reachable — the simulator-server for iOS, o // actually consumes it (mirrors the sibling `keyboard` tool's lazy services). services: (params): Record => { const device = resolveDevice(params.udid); + if (isPhysicalIos(device)) { + // Do not start WDA only to reject App Switcher, which XCTest cannot expose. + if (!PHYSICAL_IOS_BUTTON[params.button]) return {}; + return { physicalIos: physicalIosAutomationRef(device) }; + } return device.platform === "android" ? {} : { simulatorServer: simulatorServerRef(device) }; }, async execute(services, params) { @@ -80,6 +102,19 @@ Fails if the device backend is not reachable — the simulator-server for iOS, o `button '${params.button}' is not available on ${device.platform}` ); } + if (isPhysicalIos(device)) { + const name = PHYSICAL_IOS_BUTTON[params.button]; + if (!name) { + throw new UnsupportedOperationError( + "button", + device, + `button '${params.button}' is not available on physical iOS (home, power, volumeUp, volumeDown, and actionButton are supported)` + ); + } + const physicalIos = services.physicalIos as PhysicalIosAutomationApi; + await physicalIos.button(name); + return { pressed: params.button }; + } if (device.platform === "android") { // Android presses go over `adb shell input keyevent`, not the // simulator-server's HID transport, which the guest silently drops on AVDs diff --git a/packages/tool-server/src/tools/describe/contract.ts b/packages/tool-server/src/tools/describe/contract.ts index cae35e884..2966622be 100644 --- a/packages/tool-server/src/tools/describe/contract.ts +++ b/packages/tool-server/src/tools/describe/contract.ts @@ -76,7 +76,9 @@ export type DescribeSource = | "android-devtools" | "cdp-dom" | "vega-automation" - | "tv-focus"; + | "tv-focus" + | "coredevice-ax" + | "wda-ax"; // Internal shape produced by the per-platform adapters. The `tree` is consumed // by the formatter in `format-tree.ts` and then dropped before the tool replies diff --git a/packages/tool-server/src/tools/describe/format-tree.ts b/packages/tool-server/src/tools/describe/format-tree.ts index 82d3ef156..9cad8abc3 100644 --- a/packages/tool-server/src/tools/describe/format-tree.ts +++ b/packages/tool-server/src/tools/describe/format-tree.ts @@ -184,7 +184,8 @@ export function formatDescribeTree(root: DescribeNode, opts: FormatDescribeOptio opts.source === "uiautomator" || opts.source === "android-devtools" || opts.source === "cdp-dom" || - opts.source === "vega-automation" + opts.source === "vega-automation" || + opts.source === "wda-ax" ? "nested" : "flat"; const isVega = opts.source === "vega-automation"; diff --git a/packages/tool-server/src/tools/describe/platforms/ios/index.ts b/packages/tool-server/src/tools/describe/platforms/ios/index.ts index 52addf05d..1434ed4d6 100644 --- a/packages/tool-server/src/tools/describe/platforms/ios/index.ts +++ b/packages/tool-server/src/tools/describe/platforms/ios/index.ts @@ -6,12 +6,18 @@ import { nativeDevtoolsRef, NativeDevtoolsApi, } from "../../../../blueprints/native-devtools"; +import { isPhysicalIos } from "../../../../utils/device-info"; +import { + physicalIosAutomationRef, + type PhysicalIosAutomationApi, +} from "../../../../blueprints/physical-ios-automation"; import { resolveNativeTargetApp } from "../../../../utils/native-target-app"; import { isTvOsSimulator } from "../../../../utils/ios-devices"; import { parseNativeDescribeScreenResult } from "../../../native-devtools/native-describe-contract"; import { DescribeTreeData, parseDescribeResult, type DescribeNode } from "../../contract"; import { adaptAXDescribeToDescribeResult } from "./ios-ax-adapter"; import { adaptNativeDescribeToDescribeResult } from "./ios-native-adapter"; +import { adaptWdaSourceToDescribeResult } from "./ios-wda-ax-adapter"; const DEGRADED_HINT = "This simulator was not booted through argent — system dialogs and native modals may not appear. You MUST call boot-device with force=true now to restart the simulator and apply full accessibility settings before continuing."; @@ -29,6 +35,12 @@ const TVOS_HINT = "(up/down/left/right/select/back/menu/home) to move focus, and `keyboard` to type. " + "See the argent-tv-interact skill."; +// Physical iPhones expose their real nested XCTest hierarchy through WDA. +const PHYSICAL_IOS_AX_HINT = + "This is the live XCTest accessibility tree of the frontmost app (or the home screen), read through " + + "WebDriverAgent. Labels, values, traits, reading order, and element frames come from the physical " + + "device and use the same normalized coordinate space as gesture-tap and gesture-swipe."; + // Apple system apps (`com.apple.*`) can never load argent's injected dylib, so // the native-devtools fallback can't read their view hierarchy and restarting // them would never help — returning `should_restart` here puts the agent in an @@ -75,6 +87,22 @@ export async function describeIos( params: DescribeIosParams, options: DescribeIosOptions = {} ): Promise { + // Physical iPhones are read through the same persistent WDA session used for + // control. Source and frames come from XCTest and work in apps and SpringBoard. + if (isPhysicalIos(device)) { + const ref = physicalIosAutomationRef(device); + const physicalIos = await registry.resolveService( + ref.urn, + ref.options + ); + const [source, screen] = await Promise.all([physicalIos.source(), physicalIos.windowSize()]); + return { + tree: adaptWdaSourceToDescribeResult(source, screen), + source: "wda-ax", + hint: PHYSICAL_IOS_AX_HINT, + }; + } + // tvOS short-circuit: the focus-engine accessibility tree is served by the // tv-control daemons, not the iOS ax-service. Without this, describe would // try to spawn ax-service inside the Apple TV sim, time out on the daemon diff --git a/packages/tool-server/src/tools/describe/platforms/ios/ios-coredevice-ax-adapter.ts b/packages/tool-server/src/tools/describe/platforms/ios/ios-coredevice-ax-adapter.ts new file mode 100644 index 000000000..77abfc0a5 --- /dev/null +++ b/packages/tool-server/src/tools/describe/platforms/ios/ios-coredevice-ax-adapter.ts @@ -0,0 +1,123 @@ +import type { CoreDeviceAxTree } from "../../../../blueprints/core-device"; +import { parseDescribeResult, type DescribeNode } from "../../contract"; + +/** + * Adapts a physical iPhone's on-screen accessibility tree (from the iOS-26+ + * axAudit service, read app-free over CoreDevice) into a describe tree. + * + * The audit gives a rich VoiceOver caption (label + value + traits) and reading + * order for EVERY on-screen element, but Apple doesn't expose per-element + * geometry on hardware — only the subset of elements the accessibility audit + * flags carry an on-screen rect. So real frames are used where present, and the + * rest are interpolated from their reading-order neighbours (good enough to tap + * a row in a vertical list; the tool's hint says to confirm with screenshot). + */ + +// AX trait tokens that appear (comma-separated) at the tail of a VoiceOver +// caption, mapped to a describe role. Order matters: the first structural trait +// found wins. +const TRAIT_ROLE: Array<[RegExp, string]> = [ + [/^Button$/i, "AXButton"], + [/^Link$/i, "AXLink"], + [/^Header$/i, "AXHeader"], + [/^(Toggle|Switch)$/i, "AXSwitch"], + [/^Adjustable$/i, "AXSlider"], + [/^Search Field$/i, "AXSearchField"], + [/^Text Field$/i, "AXTextField"], + [/^Tab$/i, "AXTab"], + [/^Image$/i, "AXImage"], +]; +// Trailing tokens that are traits/states (stripped from the label). +const TRAIT_TOKEN = + /^(Button|Link|Header|Toggle|Switch|Adjustable|Search Field|Text Field|Tab|Image|Selected|Not Selected|Dimmed|Disabled)$/i; + +function parseCaption(caption: string): { label: string; role: string } { + const tokens = caption.split(/,\s*/).filter((t) => t.length > 0); + let role = "AXStaticText"; + for (const [re, r] of TRAIT_ROLE) { + if (tokens.some((t) => re.test(t))) { + role = r; + break; + } + } + // Drop trailing trait/state tokens to get a cleaner label; keep the full + // caption if that would leave nothing. + let end = tokens.length; + while (end > 0 && TRAIT_TOKEN.test(tokens[end - 1])) end--; + const label = (end > 0 ? tokens.slice(0, end) : tokens).join(", ") || caption; + return { label, role }; +} + +const RECT_RE = /-?\d+(?:\.\d+)?/g; + +/** Parse "{{x, y}, {w, h}}" (points) → normalized frame, or null. */ +function parseRect(rect: string | undefined, sw: number, sh: number): DescribeNode["frame"] | null { + if (!rect || sw <= 0 || sh <= 0) return null; + const nums = rect.match(RECT_RE); + if (!nums || nums.length < 4) return null; + const [x, y, w, h] = nums.slice(0, 4).map(Number); + const clamp = (v: number) => Math.max(0, Math.min(1, v)); + return { + x: clamp(x / sw), + y: clamp(y / sh), + width: clamp(w / sw), + height: clamp(h / sh), + }; +} + +const MARGIN_X = 0.04; +const APPROX_HEIGHT = 0.05; + +/** Full-width approximate frame centred at normalized y. */ +function approxFrame(yCenter: number): DescribeNode["frame"] { + const y = Math.max(0, Math.min(1 - APPROX_HEIGHT, yCenter - APPROX_HEIGHT / 2)); + return { x: MARGIN_X, y, width: 1 - 2 * MARGIN_X, height: APPROX_HEIGHT }; +} + +/** + * Fill frames for elements the audit didn't give a rect: interpolate each gap's + * y-centres between the nearest real rects above and below (reading order), so a + * list row lands roughly where it should. Falls back to an even top-to-bottom + * spread when there are no anchoring rects. + */ +function fillFrames(frames: Array): DescribeNode["frame"][] { + const n = frames.length; + const yc = (f: DescribeNode["frame"]) => f.y + f.height / 2; + const out = frames.slice(); + for (let i = 0; i < n; i++) { + if (out[i]) continue; + let prev = i - 1; + while (prev >= 0 && !out[prev]) prev--; + let next = i + 1; + while (next < n && !out[next]) next++; + const top = prev >= 0 ? yc(out[prev]!) : 0.06; + const bottom = next < n ? yc(out[next]!) : 0.94; + const span = next < n ? next : n; // denominator for even spread in the run + const start = prev >= 0 ? prev : -1; + const frac = (i - start) / (span - start); + out[i] = approxFrame(top + (bottom - top) * frac); + } + return out as DescribeNode["frame"][]; +} + +export function adaptCoreDeviceAxToDescribeResult(tree: CoreDeviceAxTree): DescribeNode { + const sw = tree.screen?.w ?? 0; + const sh = tree.screen?.h ?? 0; + const els = tree.elements ?? []; + + const rectFrames = els.map((e) => parseRect(e.rect, sw, sh)); + const frames = fillFrames(rectFrames); + + const children: DescribeNode[] = els.map((e, i) => { + const { label, role } = parseCaption(e.caption ?? ""); + const node: DescribeNode = { role, frame: frames[i], children: [] }; + if (label) node.label = label; + return node; + }); + + return parseDescribeResult({ + role: "AXGroup", + frame: { x: 0, y: 0, width: 1, height: 1 }, + children, + }); +} diff --git a/packages/tool-server/src/tools/describe/platforms/ios/ios-wda-ax-adapter.ts b/packages/tool-server/src/tools/describe/platforms/ios/ios-wda-ax-adapter.ts new file mode 100644 index 000000000..1e029c801 --- /dev/null +++ b/packages/tool-server/src/tools/describe/platforms/ios/ios-wda-ax-adapter.ts @@ -0,0 +1,104 @@ +import { XMLParser } from "fast-xml-parser"; +import { parseDescribeResult, type DescribeNode } from "../../contract"; + +type XmlNode = Record; + +const parser = new XMLParser({ + ignoreAttributes: false, + attributeNamePrefix: "", + parseAttributeValue: false, + trimValues: false, +}); + +function numeric(value: unknown): number { + const parsed = Number(value); + return Number.isFinite(parsed) ? parsed : 0; +} + +function bool(value: unknown): boolean | undefined { + if (value === "true" || value === true) return true; + if (value === "false" || value === false) return false; + return undefined; +} + +function childEntries(node: XmlNode): Array<[string, XmlNode]> { + const children: Array<[string, XmlNode]> = []; + for (const [key, value] of Object.entries(node)) { + if (!key.startsWith("XCUIElementType")) continue; + for (const item of Array.isArray(value) ? value : [value]) { + if (item && typeof item === "object") children.push([key, item as XmlNode]); + } + } + return children; +} + +function firstElement(value: unknown): [string, XmlNode] | undefined { + if (!value || typeof value !== "object") return undefined; + for (const [key, item] of Object.entries(value as XmlNode)) { + if (key.startsWith("XCUIElementType") && item && typeof item === "object") { + const node = (Array.isArray(item) ? item[0] : item) as XmlNode; + return [key, node]; + } + const nested = firstElement(item); + if (nested) return nested; + } + return undefined; +} + +function adaptNode(type: string, node: XmlNode, screenWidth: number, screenHeight: number): DescribeNode { + const rawX = numeric(node.x); + const rawY = numeric(node.y); + const rawWidth = Math.max(0, numeric(node.width)); + const rawHeight = Math.max(0, numeric(node.height)); + const x1 = Math.max(0, Math.min(screenWidth, rawX)); + const y1 = Math.max(0, Math.min(screenHeight, rawY)); + const x2 = Math.max(x1, Math.min(screenWidth, rawX + rawWidth)); + const y2 = Math.max(y1, Math.min(screenHeight, rawY + rawHeight)); + const label = typeof node.label === "string" && node.label ? node.label : undefined; + const name = typeof node.name === "string" && node.name ? node.name : undefined; + const identifier = + typeof node.identifier === "string" && node.identifier + ? node.identifier + : name && name !== label + ? name + : undefined; + const value = typeof node.value === "string" && node.value ? node.value : undefined; + const enabled = bool(node.enabled); + const focused = bool(node.focused); + const selected = bool(node.selected); + + return { + role: type.replace(/^XCUIElementType/, "AX") || "AXUnknown", + frame: { + x: screenWidth > 0 ? x1 / screenWidth : 0, + y: screenHeight > 0 ? y1 / screenHeight : 0, + width: screenWidth > 0 ? (x2 - x1) / screenWidth : 0, + height: screenHeight > 0 ? (y2 - y1) / screenHeight : 0, + }, + children: childEntries(node).map(([childType, child]) => + adaptNode(childType, child, screenWidth, screenHeight) + ), + ...(label ? { label } : {}), + ...(identifier ? { identifier } : {}), + ...(value ? { value } : {}), + ...(enabled === false ? { disabled: true } : {}), + ...(focused != null ? { focused } : {}), + ...(selected != null ? { selected } : {}), + }; +} + +/** Convert WebDriverAgent's exact XCTest hierarchy into Argent's normalized describe tree. */ +export function adaptWdaSourceToDescribeResult( + xml: string, + screen: { width: number; height: number } +): DescribeNode { + const root = firstElement(parser.parse(xml)); + if (!root) { + return parseDescribeResult({ + role: "AXApplication", + frame: { x: 0, y: 0, width: 1, height: 1 }, + children: [], + }); + } + return parseDescribeResult(adaptNode(root[0], root[1], screen.width, screen.height)); +} diff --git a/packages/tool-server/src/tools/device-logs/device-logs-start.ts b/packages/tool-server/src/tools/device-logs/device-logs-start.ts new file mode 100644 index 000000000..22fccd195 --- /dev/null +++ b/packages/tool-server/src/tools/device-logs/device-logs-start.ts @@ -0,0 +1,39 @@ +import { z } from "zod"; +import type { ServiceRef, ToolDefinition } from "@argent/registry"; +import { + deviceLogSessionRef, + type DeviceLogSessionApi, +} from "../../blueprints/device-log-session"; +import { resolveDevice } from "../../utils/device-info"; + +const schema = z.object({ + device_id: z.string().describe("Physical iOS device or iOS simulator id from list-devices."), + process: z + .string() + .optional() + .describe("Optional process name filter, for example Maps. Omit to capture all device logs."), +}); + +const capability = { apple: { simulator: true, device: true } } as const; + +export const deviceLogsStartTool: ToolDefinition< + z.infer, + { status: "recording"; outputFile: string; startedAtMs: number } +> = { + id: "device-logs-start", + capability, + description: + "Start continuous Apple unified-log capture for an iOS simulator or physical iPhone. Keep it running while gestures and native profiling execute, then call device-logs-stop to materialize the log artifact.", + zodSchema: schema, + services: (params): Record => { + const device = resolveDevice(params.device_id); + return { logs: deviceLogSessionRef(device) }; + }, + async execute(services, params) { + const logs = services.logs as DeviceLogSessionApi; + const result = await logs.start({ + process: params.process, + }); + return { status: "recording", ...result }; + }, +}; diff --git a/packages/tool-server/src/tools/device-logs/device-logs-stop.ts b/packages/tool-server/src/tools/device-logs/device-logs-stop.ts new file mode 100644 index 000000000..cf7dbb27c --- /dev/null +++ b/packages/tool-server/src/tools/device-logs/device-logs-stop.ts @@ -0,0 +1,50 @@ +import { z } from "zod"; +import type { ToolDefinition } from "@argent/registry"; +import { + deviceLogSessionRef, + type DeviceLogSessionApi, +} from "../../blueprints/device-log-session"; +import { resolveDevice } from "../../utils/device-info"; +import { isPhysicalIos } from "../../utils/device-info"; +import { + physicalIosAutomationRef, + type PhysicalIosAutomationApi, +} from "../../blueprints/physical-ios-automation"; +import { requireArtifacts, type ArtifactHandle } from "../../artifacts"; + +const schema = z.object({ + device_id: z.string().describe("Physical iOS device or iOS simulator id from list-devices."), +}); + +const capability = { apple: { simulator: true, device: true } } as const; + +export const deviceLogsStopTool: ToolDefinition< + z.infer, + { status: "stopped"; logs: ArtifactHandle; lineCount: number; durationMs: number } +> = { + id: "device-logs-stop", + capability, + description: + "Stop a device-logs-start capture and return the captured Apple unified logs as a downloadable text artifact.", + zodSchema: schema, + services: (params) => { + const device = resolveDevice(params.device_id); + return { + logs: deviceLogSessionRef(device), + ...(isPhysicalIos(device) + ? { physicalIos: physicalIosAutomationRef(device) } + : {}), + }; + }, + async execute(services, _params, ctx) { + await (services.physicalIos as PhysicalIosAutomationApi | undefined)?.flushControls(); + const result = await (services.logs as DeviceLogSessionApi).stop(); + const logs = await requireArtifacts(ctx).register(result.outputFile, { mimeType: "text/plain" }); + return { + status: "stopped", + logs, + lineCount: result.lineCount, + durationMs: result.durationMs, + }; + }, +}; diff --git a/packages/tool-server/src/tools/devices/boot-device.ts b/packages/tool-server/src/tools/devices/boot-device.ts index a0b66fb21..3eade0a59 100644 --- a/packages/tool-server/src/tools/devices/boot-device.ts +++ b/packages/tool-server/src/tools/devices/boot-device.ts @@ -31,7 +31,16 @@ import { import { ensureDep } from "../../utils/check-deps"; import { linuxBootDiagnostics } from "../../utils/linux-preflight"; import { listIosSimulators } from "../../utils/ios-devices"; -import { classifyDevice, stripRemotePrefix } from "../../utils/device-info"; +import { + isPhysicalIosUdid, + classifyDevice, + resolveDevice, + stripRemotePrefix, +} from "../../utils/device-info"; +import { + physicalIosAutomationRef, + type PhysicalIosAutomationApi, +} from "../../blueprints/physical-ios-automation"; import { simctlBoot as simRemoteBoot, simctlBootstatus as simRemoteBootstatus, @@ -58,7 +67,7 @@ const zodSchema = z.object({ .string() .optional() .describe( - "iOS: simulator UDID to boot (from `list-devices`). Provide exactly one of `udid`, `avdName`, `vvdImage`, or `electronAppPath`." + "iOS: simulator or connected physical-iPhone UDID from `list-devices`. Simulators are booted; physical phones are prepared for WebDriverAgent control without rebooting. Provide exactly one of `udid`, `avdName`, `vvdImage`, or `electronAppPath`." ), avdName: z .string() @@ -477,6 +486,16 @@ async function bootIos( } ); } + + // A physical iPhone is already powered on — there is nothing to "boot". + // Treat this as the explicit preparation step: build/sign WDA if needed and + // establish the persistent XCTest transport so the first control is warm. + if (isPhysicalIosUdid(udid)) { + const ref = physicalIosAutomationRef(resolveDevice(udid)); + await registry.resolveService(ref.urn, ref.options); + return { platform: "ios", udid, booted: true }; + } + await ensureDep("xcrun"); const simMatch = await listIosSimulators() @@ -1384,8 +1403,8 @@ export function createBootDeviceTool( ): ToolDefinition { return { id: "boot-device", - description: `Start an iOS simulator, launch an Android emulator, start a Vega (Fire TV) Virtual Device, or spawn an Electron app and wait until it is ready to accept interactions. -Pick the platform by which argument you pass: 'udid' for an iOS simulator from list-devices, 'avdName' for an Android AVD (a serial is assigned automatically), 'vvdImage' for a Vega VVD (the 'vvdImage' of a vega device from list-devices, e.g. 'tv'), or 'electronAppPath' for an Electron app (a CDP remote-debugging port is picked automatically, or pass 'electronPort' to fix one). + description: `Start an iOS simulator, prepare a connected physical iPhone for WebDriverAgent control, launch an Android emulator, start a Vega (Fire TV) Virtual Device, or spawn an Electron app and wait until it is ready to accept interactions. +Pick the platform by which argument you pass: 'udid' for an iOS simulator or physical iPhone from list-devices, 'avdName' for an Android AVD (a serial is assigned automatically), 'vvdImage' for a Vega VVD (the 'vvdImage' of a vega device from list-devices, e.g. 'tv'), or 'electronAppPath' for an Electron app (a CDP remote-debugging port is picked automatically, or pass 'electronPort' to fix one). A physical iPhone is not rebooted; Argent builds/signs/caches WebDriverAgent on first use and reuses the session. Use at the start of a session once you have picked a target. Returns a tagged payload: { platform: 'ios', udid, booted } or { platform: 'android', serial, avdName, booted } or { platform: 'vega', serial, vvdImage, booted } or { platform: 'chromium', id, port, pid, booted } (an Electron app boots as a Chromium/CDP device). Android boots take 2–10 minutes depending on machine and cold/warm state; the tool transparently hot-boots from the AVD's default_boot snapshot when usable and falls back to cold boot otherwise. Vega starts the single SDK-managed VVD via the vega CLI (~10s) and returns once it reports running. If an Android/Electron boot stage fails, the tool terminates the device it spawned so the next retry starts clean.`, diff --git a/packages/tool-server/src/tools/devices/list-devices.ts b/packages/tool-server/src/tools/devices/list-devices.ts index 070b46047..85fb6b079 100644 --- a/packages/tool-server/src/tools/devices/list-devices.ts +++ b/packages/tool-server/src/tools/devices/list-devices.ts @@ -1,5 +1,6 @@ import { z } from "zod"; import type { ToolDefinition } from "@argent/registry"; +import { isFlagEnabled } from "@argent/configuration-core"; import { listAndroidDevices, listAvds, @@ -7,7 +8,7 @@ import { ADB_DEVICES_TIMEOUT_MS, } from "../../utils/adb"; import { listRunningVvdConsolePorts } from "../../utils/vega-process"; -import { listIosSimulators, type IosSimulator } from "../../utils/ios-devices"; +import { listIosSimulators, listIosDevices } from "../../utils/ios-devices"; import { simctlListDevices } from "../../utils/sim-remote"; import { withRemotePrefix } from "../../utils/device-info"; import { discoverChromiumDevices, type ChromiumDevice } from "../../utils/chromium-discovery"; @@ -16,7 +17,24 @@ import { filterVvdShadowsFromAndroid, type VegaDevice, } from "../../utils/vega-devices"; -type IosDevice = IosSimulator & { platform: "ios" }; + +const PHYSICAL_IOS_FLAG = "physical-ios-devices"; + +type IosDevice = { + platform: "ios"; + udid: string; + name: string; + state: string; + // "simulator" for an `xcrun simctl` simulator, "device" for a physical iPhone + // discovered via `xcrun devicectl` and driven over CoreDevice (pymobiledevice3). + kind: "simulator" | "device"; + // simulators only (the iOS runtime, e.g. "com.apple.CoreSimulator.SimRuntime.iOS-18-5") + runtime?: string; + // simulators only: "tv" for an Apple TV simulator, "mobile" for iOS/iPadOS. + runtimeKind?: "mobile" | "tv"; + // physical devices only (Apple product type, e.g. "iPhone15,4") + productType?: string | null; +}; type IosRemoteDevice = { platform: "ios-remote"; @@ -42,15 +60,18 @@ type AndroidDevice = { runtimeKind?: "mobile" | "tv"; }; -type ListDevicesResult = { +export type ListDevicesResult = { devices: Array; avds: Array<{ name: string }>; }; +// A simulator is ready when "Booted"; a physical device is ready when "connected". +const iosReady = (d: IosDevice): boolean => d.state === "Booted" || d.state === "connected"; + function sortIos(a: IosDevice, b: IosDevice): number { - const aBooted = a.state === "Booted" ? 0 : 1; - const bBooted = b.state === "Booted" ? 0 : 1; - if (aBooted !== bBooted) return aBooted - bBooted; + const aReady = iosReady(a) ? 0 : 1; + const bReady = iosReady(b) ? 0 : 1; + if (aReady !== bReady) return aReady - bReady; const aIpad = a.name.includes("iPad") ? 1 : 0; const bIpad = b.name.includes("iPad") ? 1 : 0; return aIpad - bIpad; @@ -70,6 +91,7 @@ function sortAndroid(a: AndroidDevice, b: AndroidDevice): number { function readinessRank( d: IosDevice | IosRemoteDevice | AndroidDevice | ChromiumDevice | VegaDevice ): number { + if (d.platform === "ios") return iosReady(d) ? 0 : 1; if (d.platform === "android") return d.state === "device" ? 0 : 1; if (d.platform === "vega") return d.state === "running" || d.state === "device" ? 0 : 1; if (d.platform === "chromium") return 0; // Chromium entries are only listed when their CDP is responsive @@ -196,6 +218,7 @@ export const listDevicesTool: ToolDefinition, ListDevicesR Use at the start of a session to pick a target id ('udid' for iOS entries, 'serial' for Android/Vega entries, 'id' for Chromium) to pass to interaction tools, and to see which targets are already running. Returns { devices, avds } where each device carries a 'platform' discriminator ('ios', 'android', 'chromium', or 'vega'); 'avds' lists Android AVDs bootable via boot-device. A Vega VVD is listed under 'devices' whether running or stopped (state 'running'/'stopped'); start a stopped one with boot-device using its 'vvdImage'. Android entries also carry a 'kind' ('emulator' for a local AVD, 'device' for a physical phone connected over USB / wireless adb) — physical phones are detected from \`adb devices\` (any serial that is not an \`emulator-*\` one) and are driven through the same interaction tools as emulators; they do not need boot-device (just connect the phone with USB debugging authorised). +iOS entries likewise carry a 'kind' ('simulator', or 'device' for a connected physical iPhone). Physical iOS devices require the 'physical-ios-devices' flag (\`argent enable physical-ios-devices\`), an unlocked/trusted phone with Developer Mode, and an Apple Development team configured in Xcode. Argent prepares a signed WebDriverAgent session automatically and supports touch/multi-touch, keyboard, screenshots, accessibility, app lifecycle, logs, and native profiling through the same tool interfaces as a simulator. TV targets are tagged with runtimeKind 'tv' (Apple TV simulators on iOS, Android TV / leanback devices on Android) — these are focus-driven, not touch-driven: use \`describe\` to read focus, \`tv-remote\` for remote presses (up/down/left/right/select/back/menu/home), and \`keyboard\` to type, rather than the coordinate/gesture tools. Chromium apps are discovered by probing CDP debugging ports (default 9222; extend via the ARGENT_CHROMIUM_PORTS= env var). They must already be running with --remote-debugging-port= — use boot-device with electronAppPath to launch one. Booted/ready devices are listed first. Platforms whose CLI is unavailable are silently omitted — an empty result usually means xcode-select, Android platform-tools, or the Vega SDK is not installed.`, @@ -213,9 +236,15 @@ Booted/ready devices are listed first. Platforms whose CLI is unavailable are si // timer is cleared on the fast happy path). The deadline only substitutes a // fallback on *slowness*; a rejection still propagates exactly as before — so the // `.catch(() => [])` wrappers (and the lack of one on iOS/AVDs) are unchanged. - const [ios, iosRemote, android, avds, chromium, vega] = await Promise.all([ + const physicalIosEnabled = isFlagEnabled(PHYSICAL_IOS_FLAG); + const [ios, iosRemote, iosPhysical, android, avds, chromium, vega] = await Promise.all([ withDeadline(listIosSimulators(), [], "ios"), withDeadline(listRemoteIosSimulators(), [], "ios-remote"), + withDeadline( + physicalIosEnabled ? listIosDevices().catch(() => []) : Promise.resolve([]), + [], + "ios-physical" + ), withDeadline( // Opt into runtimeKind enrichment (list-devices surfaces TV vs mobile to // the agent, so the extra feature probe per device is warranted here — the @@ -240,7 +269,29 @@ Booted/ready devices are listed first. Platforms whose CLI is unavailable are si "vega" ), ]); - const iosTagged: IosDevice[] = ios.map((s) => ({ platform: "ios", ...s })); + const iosTagged: IosDevice[] = [ + ...ios.map( + (s): IosDevice => ({ + platform: "ios", + kind: "simulator", + udid: s.udid, + name: s.name, + state: s.state, + runtime: s.runtime, + runtimeKind: s.runtimeKind, + }) + ), + ...iosPhysical.map( + (d): IosDevice => ({ + platform: "ios", + kind: "device", + udid: d.udid, + name: d.name, + state: d.state, + productType: d.productType, + }) + ), + ]; iosTagged.sort(sortIos); iosRemote.sort(sortIosRemote); const androidTagged: AndroidDevice[] = android.map((d) => ({ diff --git a/packages/tool-server/src/tools/gesture-custom/index.ts b/packages/tool-server/src/tools/gesture-custom/index.ts index 14ea892a1..8a5670837 100644 --- a/packages/tool-server/src/tools/gesture-custom/index.ts +++ b/packages/tool-server/src/tools/gesture-custom/index.ts @@ -1,7 +1,11 @@ import { z } from "zod"; -import type { ToolCapability, ToolDefinition } from "@argent/registry"; +import type { ServiceRef, ToolCapability, ToolDefinition } from "@argent/registry"; import { simulatorServerRef, type SimulatorServerApi } from "../../blueprints/simulator-server"; -import { resolveDevice } from "../../utils/device-info"; +import { + physicalIosAutomationRef, + type PhysicalIosAutomationApi, +} from "../../blueprints/physical-ios-automation"; +import { isPhysicalIos, resolveDevice } from "../../utils/device-info"; import { sendCommand } from "../../utils/simulator-client"; import { interpolateEvents } from "../../utils/gesture-utils"; @@ -78,16 +82,26 @@ Example pinch-to-zoom (with interpolate:10 for smoothness): interpolate: 10`, zodSchema, capability, - services: (params) => ({ - simulatorServer: simulatorServerRef(resolveDevice(params.udid)), - }), + services: (params): Record => { + const device = resolveDevice(params.udid); + return isPhysicalIos(device) + ? { physicalIos: physicalIosAutomationRef(device) } + : { simulatorServer: simulatorServerRef(device) }; + }, async execute(services, params) { - const api = services.simulatorServer as SimulatorServerApi; const events = params.interpolate && params.interpolate > 0 ? interpolateEvents(params.events, params.interpolate) : params.events; + if (isPhysicalIos(resolveDevice(params.udid))) { + const physicalIos = services.physicalIos as PhysicalIosAutomationApi; + await physicalIos.touch(events); + return { events: events.length }; + } + + const api = services.simulatorServer as SimulatorServerApi; + for (const event of events) { await sleep(event.delayMs ?? 16); sendCommand(api, { diff --git a/packages/tool-server/src/tools/gesture-pinch/index.ts b/packages/tool-server/src/tools/gesture-pinch/index.ts index 5efbcee38..89c880f23 100644 --- a/packages/tool-server/src/tools/gesture-pinch/index.ts +++ b/packages/tool-server/src/tools/gesture-pinch/index.ts @@ -1,7 +1,11 @@ import { z } from "zod"; -import type { ToolCapability, ToolDefinition } from "@argent/registry"; +import type { ServiceRef, ToolCapability, ToolDefinition } from "@argent/registry"; import { simulatorServerRef, type SimulatorServerApi } from "../../blueprints/simulator-server"; -import { resolveDevice } from "../../utils/device-info"; +import { + physicalIosAutomationRef, + type PhysicalIosAutomationApi, +} from "../../blueprints/physical-ios-automation"; +import { isPhysicalIos, resolveDevice } from "../../utils/device-info"; import { sendTouchEvent } from "../../utils/gesture-utils"; import { sleep } from "../../utils/timing"; @@ -63,11 +67,13 @@ Auto-generates interpolated frames at ~60fps. The angle parameter controls the a Use when you need to zoom in or out on a map, image, or zoomable view. Returns { pinched: true, timestampMs }. Fails if the simulator-server / emulator backend is not reachable for the given device.`, zodSchema, capability, - services: (params) => ({ - simulatorServer: simulatorServerRef(resolveDevice(params.udid)), - }), + services: (params): Record => { + const device = resolveDevice(params.udid); + return isPhysicalIos(device) + ? { physicalIos: physicalIosAutomationRef(device) } + : { simulatorServer: simulatorServerRef(device) }; + }, async execute(services, params) { - const api = services.simulatorServer as SimulatorServerApi; const duration = params.durationMs ?? 300; const steps = Math.max(1, Math.round(duration / 16)); const angleDeg = params.angle ?? 0; @@ -76,6 +82,17 @@ Use when you need to zoom in or out on a map, image, or zoomable view. Returns { const sinA = Math.sin(angleRad); let timestampMs = 0; + const physicalEvents: Array<{ + type: "Down" | "Move" | "Up"; + x: number; + y: number; + x2: number; + y2: number; + delayMs: number; + }> = []; + + const physical = isPhysicalIos(resolveDevice(params.udid)); + const api = physical ? undefined : (services.simulatorServer as SimulatorServerApi); for (let i = 0; i <= steps; i++) { const t = i / steps; @@ -90,8 +107,16 @@ Use when you need to zoom in or out on a map, image, or zoomable view. Returns { const type = i === 0 ? "Down" : i === steps ? "Up" : "Move"; if (i === 0) timestampMs = Date.now(); - sendTouchEvent(api, type, x1, y1, x2, y2); - if (i < steps) await sleep(16); + if (physical) { + physicalEvents.push({ type, x: x1, y: y1, x2, y2, delayMs: i === 0 ? 0 : 16 }); + } else { + sendTouchEvent(api!, type, x1, y1, x2, y2); + if (i < steps) await sleep(16); + } + } + + if (physical) { + await (services.physicalIos as PhysicalIosAutomationApi).touch(physicalEvents); } return { pinched: true, timestampMs }; diff --git a/packages/tool-server/src/tools/gesture-rotate/index.ts b/packages/tool-server/src/tools/gesture-rotate/index.ts index d93b75e6b..a089f16a9 100644 --- a/packages/tool-server/src/tools/gesture-rotate/index.ts +++ b/packages/tool-server/src/tools/gesture-rotate/index.ts @@ -1,7 +1,11 @@ import { z } from "zod"; -import type { ToolCapability, ToolDefinition } from "@argent/registry"; +import type { ServiceRef, ToolCapability, ToolDefinition } from "@argent/registry"; import { simulatorServerRef, type SimulatorServerApi } from "../../blueprints/simulator-server"; -import { resolveDevice } from "../../utils/device-info"; +import { + physicalIosAutomationRef, + type PhysicalIosAutomationApi, +} from "../../blueprints/physical-ios-automation"; +import { isPhysicalIos, resolveDevice } from "../../utils/device-info"; import { sendTouchEvent } from "../../utils/gesture-utils"; import { sleep } from "../../utils/timing"; @@ -53,15 +57,27 @@ Unlike gesture-pinch which moves fingers linearly to zoom, this orbits fingers i Use when you need to rotate a map, image picker, or any rotateable UI element. Returns { rotated: true, timestampMs }. Fails if the simulator-server / emulator backend is not reachable for the given device.`, zodSchema, capability, - services: (params) => ({ - simulatorServer: simulatorServerRef(resolveDevice(params.udid)), - }), + services: (params): Record => { + const device = resolveDevice(params.udid); + return isPhysicalIos(device) + ? { physicalIos: physicalIosAutomationRef(device) } + : { simulatorServer: simulatorServerRef(device) }; + }, async execute(services, params) { - const api = services.simulatorServer as SimulatorServerApi; const duration = params.durationMs ?? 300; const steps = Math.max(1, Math.round(duration / 16)); let timestampMs = 0; + const physicalEvents: Array<{ + type: "Down" | "Move" | "Up"; + x: number; + y: number; + x2: number; + y2: number; + delayMs: number; + }> = []; + const physical = isPhysicalIos(resolveDevice(params.udid)); + const api = physical ? undefined : (services.simulatorServer as SimulatorServerApi); for (let i = 0; i <= steps; i++) { const t = i / steps; @@ -76,8 +92,16 @@ Use when you need to rotate a map, image picker, or any rotateable UI element. R const type = i === 0 ? "Down" : i === steps ? "Up" : "Move"; if (i === 0) timestampMs = Date.now(); - sendTouchEvent(api, type, x1, y1, x2, y2); - if (i < steps) await sleep(16); + if (physical) { + physicalEvents.push({ type, x: x1, y: y1, x2, y2, delayMs: i === 0 ? 0 : 16 }); + } else { + sendTouchEvent(api!, type, x1, y1, x2, y2); + if (i < steps) await sleep(16); + } + } + + if (physical) { + await (services.physicalIos as PhysicalIosAutomationApi).touch(physicalEvents); } return { rotated: true, timestampMs }; diff --git a/packages/tool-server/src/tools/gesture-swipe/index.ts b/packages/tool-server/src/tools/gesture-swipe/index.ts index fcc4a31a6..a285c62dc 100644 --- a/packages/tool-server/src/tools/gesture-swipe/index.ts +++ b/packages/tool-server/src/tools/gesture-swipe/index.ts @@ -1,7 +1,11 @@ import { z } from "zod"; -import type { ToolCapability, ToolDefinition } from "@argent/registry"; +import type { ServiceRef, ToolCapability, ToolDefinition } from "@argent/registry"; import { simulatorServerRef, type SimulatorServerApi } from "../../blueprints/simulator-server"; -import { resolveDevice } from "../../utils/device-info"; +import { + physicalIosAutomationRef, + type PhysicalIosAutomationApi, +} from "../../blueprints/physical-ios-automation"; +import { resolveDevice, isPhysicalIos } from "../../utils/device-info"; import { sendCommand } from "../../utils/simulator-client"; const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); @@ -40,17 +44,27 @@ export const gestureSwipeTool: ToolDefinition = { description: `Execute a smooth swipe / drag touch gesture between two points on the device (iOS simulator or Android emulator). All from/to positions are normalized 0.0–1.0 (fractions of screen width/height, not pixels), same as gesture-tap. Generates interpolated Move events for a natural feel (~60fps). Swipe up (fromY > toY) to scroll content down. -Use when you need to scroll a list, dismiss a modal, drag an element, or navigate between pages. Not supported on Chromium — use gesture-scroll there instead. Returns { swiped: true, timestampMs }. Fails if the simulator-server / emulator backend is not reachable for the given device.`, +Use when you need to scroll a list, dismiss a modal, drag an element, or navigate between pages. Not supported on Chromium — use gesture-scroll there instead. Returns { swiped: true, timestampMs }. On a physical iPhone, swipes register on the persistent WebDriverAgent queue. Fails if the device backend is not reachable.`, alwaysLoad: true, searchHint: "swipe scroll drag pan gesture device simulator emulator touch move", zodSchema, capability, - services: (params) => ({ - simulatorServer: simulatorServerRef(resolveDevice(params.udid)), - }), + services: (params): Record => { + const device = resolveDevice(params.udid); + if (isPhysicalIos(device)) { + return { physicalIos: physicalIosAutomationRef(device) }; + } + return { simulatorServer: simulatorServerRef(device) }; + }, async execute(services, params) { const duration = params.durationMs ?? 300; const timestampMs = Date.now(); + const device = resolveDevice(params.udid); + if (isPhysicalIos(device)) { + const physicalIos = services.physicalIos as PhysicalIosAutomationApi; + await physicalIos.swipe(params.fromX, params.fromY, params.toX, params.toY, duration); + return { swiped: true, timestampMs }; + } const api = services.simulatorServer as SimulatorServerApi; const steps = Math.max(1, Math.round(duration / 16)); diff --git a/packages/tool-server/src/tools/gesture-tap/index.ts b/packages/tool-server/src/tools/gesture-tap/index.ts index 0654b339d..5499833e3 100644 --- a/packages/tool-server/src/tools/gesture-tap/index.ts +++ b/packages/tool-server/src/tools/gesture-tap/index.ts @@ -2,7 +2,11 @@ import { z } from "zod"; import type { ServiceRef, ToolCapability, ToolDefinition } from "@argent/registry"; import { simulatorServerRef, type SimulatorServerApi } from "../../blueprints/simulator-server"; import { chromiumCdpRef, type ChromiumCdpApi } from "../../blueprints/chromium-cdp"; -import { resolveDevice } from "../../utils/device-info"; +import { + physicalIosAutomationRef, + type PhysicalIosAutomationApi, +} from "../../blueprints/physical-ios-automation"; +import { resolveDevice, isPhysicalIos } from "../../utils/device-info"; import { sendCommand } from "../../utils/simulator-client"; const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); @@ -44,7 +48,7 @@ export const gestureTapTool: ToolDefinition = { 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–1.0 (not pixels). 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. Use when you need to tap a button, link, or any tappable element on the screen. -Returns { tapped: true, timestampMs }. Fails if the simulator-server / emulator backend / Chromium CDP is not reachable for the given device. +Returns { tapped: true, timestampMs }. On a physical iPhone, taps register on the persistent WebDriverAgent queue. Fails if the device backend / Chromium CDP is not reachable. Before tapping, determine the correct coordinates by using discovery tools — 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`, alwaysLoad: true, searchHint: "tap press button element device simulator emulator chromium touch down up click", @@ -55,6 +59,9 @@ Before tapping, determine the correct coordinates by using discovery tools — p if (device.platform === "chromium") { return { chromium: chromiumCdpRef(device) }; } + if (isPhysicalIos(device)) { + return { physicalIos: physicalIosAutomationRef(device) }; + } return { simulatorServer: simulatorServerRef(device) }; }, async execute(services, params) { @@ -65,6 +72,11 @@ Before tapping, determine the correct coordinates by using discovery tools — p await tapChromium(chromium, params.x, params.y); return { tapped: true, timestampMs }; } + if (isPhysicalIos(device)) { + const physicalIos = services.physicalIos as PhysicalIosAutomationApi; + await physicalIos.tap(params.x, params.y); + return { tapped: true, timestampMs }; + } const api = services.simulatorServer as SimulatorServerApi; sendCommand(api, { cmd: "touch", diff --git a/packages/tool-server/src/tools/keyboard/platforms/ios.ts b/packages/tool-server/src/tools/keyboard/platforms/ios.ts index 6322fd739..0d7ae99d5 100644 --- a/packages/tool-server/src/tools/keyboard/platforms/ios.ts +++ b/packages/tool-server/src/tools/keyboard/platforms/ios.ts @@ -4,6 +4,11 @@ import { isTvOsSimulator } from "../../../utils/ios-devices"; import type { KeyboardParams, KeyboardResult } from "../types"; import { typeSimulatorServer } from "../simulator-server-keys"; import { typeTv } from "./tv"; +import { + physicalIosAutomationRef, + type PhysicalIosAutomationApi, +} from "../../../blueprints/physical-ios-automation"; +import { isPhysicalIos } from "../../../utils/device-info"; // A tvOS sim classifies as platform "ios" by UDID shape, so this branch handles // both iPhone/iPad (simulator-server typing) and Apple TV (focus-driven typing). @@ -12,10 +17,21 @@ export function makeIosImpl( registry: Registry ): PlatformImpl, KeyboardParams, KeyboardResult> { return { - handler: async (_services, params, device) => - (await isTvOsSimulator(device.id)) + handler: async (_services, params, device) => { + if (isPhysicalIos(device)) { + const ref = physicalIosAutomationRef(device); + const api = await registry.resolveService(ref.urn, ref.options); + if (params.key) await api.pressKey(params.key.toLowerCase()); + if (params.text) await api.typeText(params.text, params.delayMs); + return { + typed: params.text ?? params.key ?? "", + keys: (params.key ? 1 : 0) + (params.text?.length ?? 0), + }; + } + return (await isTvOsSimulator(device.id)) ? typeTv(registry, device, params) - : typeSimulatorServer(registry, device, params), + : typeSimulatorServer(registry, device, params); + }, }; } diff --git a/packages/tool-server/src/tools/launch-app/platforms/ios.ts b/packages/tool-server/src/tools/launch-app/platforms/ios.ts index 78900fb0d..2e55b9242 100644 --- a/packages/tool-server/src/tools/launch-app/platforms/ios.ts +++ b/packages/tool-server/src/tools/launch-app/platforms/ios.ts @@ -11,6 +11,11 @@ import { precheckNativeDevtools, type NativeDevtoolsApi, } from "../../../blueprints/native-devtools"; +import { assertPhysicalIosEnabled } from "../../../blueprints/core-device"; +import { + physicalIosAutomationRef, + type PhysicalIosAutomationApi, +} from "../../../blueprints/physical-ios-automation"; import type { PlatformImpl } from "../../../utils/cross-platform-tool"; import type { LaunchAppParams, LaunchAppResult } from "../types"; @@ -26,6 +31,48 @@ export function makeIosImpl( return { requires: ["xcrun"], handler: async (_services, params, device) => { + // Launch physical apps through devicectl while warming the WDA control + // session. The app must already be installed/signed on the device. + if (device.kind === "device") { + // launch-app shells devicectl before resolving a tool-declared service, + // so it enforces the opt-in flag itself. + assertPhysicalIosEnabled(); + try { + const ref = physicalIosAutomationRef(device); + // Warm the persistent WDA transport while devicectl launches the app. + // This moves the one-time XCTest session startup out of the first tap + // so steady-state control registration matches simulator cadence. + await Promise.all([ + registry.resolveService(ref.urn, ref.options), + execFileAsync("xcrun", [ + "devicectl", + "device", + "process", + "launch", + "--device", + params.udid, + params.bundleId, + ]), + ]); + } catch (err) { + // The dominant failure here is "app not installed/signed on the device". + // Without this wrap, devicectl's verbose multi-line blob surfaces raw as + // a 500; emit a structured FailureError with a clean message + telemetry + // instead (mirroring the simctl branch below). + throw new FailureError( + `Failed to launch ${params.bundleId} on physical iOS device ${params.udid} via devicectl — the app must already be installed and signed on the device.`, + { + error_code: FAILURE_CODES.IOS_LAUNCH_DEVICECTL_FAILED, + failure_stage: "ios_launch_app_devicectl_launch", + failure_area: "tool_server", + error_kind: "subprocess", + ...subprocessFailureMetadata(err, "xcrun_devicectl"), + }, + { cause: err instanceof Error ? err : new Error(String(err)) } + ); + } + return { launched: true, bundleId: params.bundleId }; + } const ndRef = nativeDevtoolsRef(device); const nativeDevtools = await registry.resolveService( ndRef.urn, diff --git a/packages/tool-server/src/tools/native-devtools/native-describe-screen.ts b/packages/tool-server/src/tools/native-devtools/native-describe-screen.ts index 5c454ab4b..77c67b7c1 100644 --- a/packages/tool-server/src/tools/native-devtools/native-describe-screen.ts +++ b/packages/tool-server/src/tools/native-devtools/native-describe-screen.ts @@ -41,7 +41,7 @@ type Result = export const nativeDescribeScreenTool: ToolDefinition = { id: "native-describe-screen", - capability: { apple: { simulator: true, device: true }, appleRemote: { simulator: true } }, + capability: { apple: { simulator: true, device: false }, appleRemote: { simulator: true } }, description: `Read the running app's native accessibility screen description via injected native devtools. Returns a flat list of accessibility leaf elements with: @@ -62,6 +62,9 @@ If status is restart_required: call restart-app then retry.`, }), async execute(services, params) { const device = resolveDevice(params.udid); + // Gate host deps per device kind: local sims need xcrun, remote sims route + // via sim-remote — a global `requires:["xcrun"]` would wrongly 424 a remote + // sim on an xcrun-less host, so the dep check lives here, not on the def. await ensureDeps(device.platform === "ios-remote" ? ["sim-remote"] : ["xcrun"]); const api = services.nativeDevtools as NativeDevtoolsApi; diff --git a/packages/tool-server/src/tools/native-devtools/native-devtools-status.ts b/packages/tool-server/src/tools/native-devtools/native-devtools-status.ts index 2400795eb..d82199ecb 100644 --- a/packages/tool-server/src/tools/native-devtools/native-devtools-status.ts +++ b/packages/tool-server/src/tools/native-devtools/native-devtools-status.ts @@ -30,7 +30,7 @@ type Result = export const nativeDevtoolsStatusTool: ToolDefinition = { id: "native-devtools-status", - capability: { apple: { simulator: true, device: true }, appleRemote: { simulator: true } }, + capability: { apple: { simulator: true, device: false }, appleRemote: { simulator: true } }, description: `Check whether native devtools are connected to a specific app and whether the next launch is prepared for injection. Use when you need to verify native devtools readiness before calling native-full-hierarchy, native-describe-screen, or native-network-logs. @@ -54,6 +54,9 @@ Fails if the simulator server is not running for the given UDID.`, }), async execute(services, params) { const device = resolveDevice(params.udid); + // Gate host deps per device kind: local sims need xcrun, remote sims route + // via sim-remote — a global `requires:["xcrun"]` would wrongly 424 a remote + // sim on an xcrun-less host, so the dep check lives here, not on the def. await ensureDeps(device.platform === "ios-remote" ? ["sim-remote"] : ["xcrun"]); const api = services.nativeDevtools as NativeDevtoolsApi; diff --git a/packages/tool-server/src/tools/native-devtools/native-find-views.ts b/packages/tool-server/src/tools/native-devtools/native-find-views.ts index 9533bfafa..41ce7e8c4 100644 --- a/packages/tool-server/src/tools/native-devtools/native-find-views.ts +++ b/packages/tool-server/src/tools/native-devtools/native-find-views.ts @@ -44,7 +44,7 @@ type Result = export const nativeFindViewsTool: ToolDefinition = { id: "native-find-views", - capability: { apple: { simulator: true, device: true }, appleRemote: { simulator: true } }, + capability: { apple: { simulator: true, device: false }, appleRemote: { simulator: true } }, description: `Search for specific UIViews in the running app by class name, accessibility identifier, label, tag, or React Native nativeID. Use when you need to locate a specific view by its properties without dumping the entire hierarchy. Returns { status: "ok", matches } with matching views including their frames, properties, optional ancestors, and optional children. Much more targeted than native-full-hierarchy. @@ -56,6 +56,9 @@ Fails if native devtools are not connected, the app is not running, or status is }), async execute(services, params) { const device = resolveDevice(params.udid); + // Gate host deps per device kind: local sims need xcrun, remote sims route + // via sim-remote — a global `requires:["xcrun"]` would wrongly 424 a remote + // sim on an xcrun-less host, so the dep check lives here, not on the def. await ensureDeps(device.platform === "ios-remote" ? ["sim-remote"] : ["xcrun"]); const api = services.nativeDevtools as NativeDevtoolsApi; diff --git a/packages/tool-server/src/tools/native-devtools/native-full-hierarchy.ts b/packages/tool-server/src/tools/native-devtools/native-full-hierarchy.ts index ab85f7f0b..c13550129 100644 --- a/packages/tool-server/src/tools/native-devtools/native-full-hierarchy.ts +++ b/packages/tool-server/src/tools/native-devtools/native-full-hierarchy.ts @@ -58,7 +58,7 @@ type Result = export const nativeFullHierarchyTool: ToolDefinition = { id: "native-full-hierarchy", - capability: { apple: { simulator: true, device: true }, appleRemote: { simulator: true } }, + capability: { apple: { simulator: true, device: false }, appleRemote: { simulator: true } }, description: `Get the complete UIKit view tree for the running app. WARNING: Output can be extremely large (100KB–500KB+) for complex apps, especially those built with SwiftUI. Prefer native-find-views for targeted queries. Use skipClasses / skipClassPrefixes to prune SwiftUI internal subtrees and reduce output size. Use the fields param to request only the properties you need. @@ -71,6 +71,9 @@ Fails if native devtools are not connected or the app is not running.`, }), async execute(services, params) { const device = resolveDevice(params.udid); + // Gate host deps per device kind: local sims need xcrun, remote sims route + // via sim-remote — a global `requires:["xcrun"]` would wrongly 424 a remote + // sim on an xcrun-less host, so the dep check lives here, not on the def. await ensureDeps(device.platform === "ios-remote" ? ["sim-remote"] : ["xcrun"]); const api = services.nativeDevtools as NativeDevtoolsApi; diff --git a/packages/tool-server/src/tools/native-devtools/native-network-logs.ts b/packages/tool-server/src/tools/native-devtools/native-network-logs.ts index f8cc9fd01..420b214c2 100644 --- a/packages/tool-server/src/tools/native-devtools/native-network-logs.ts +++ b/packages/tool-server/src/tools/native-devtools/native-network-logs.ts @@ -29,7 +29,7 @@ type Result = export const nativeNetworkLogsTool: ToolDefinition = { id: "native-network-logs", - capability: { apple: { simulator: true, device: true }, appleRemote: { simulator: true } }, + capability: { apple: { simulator: true, device: false }, appleRemote: { simulator: true } }, description: `Retrieve network requests captured at the native NSURLProtocol level. Unlike the JS-level network inspector (view-network-logs), this captures ALL network traffic from the app including native modules, Swift/Objective-C networking, and background transfers that bypass JS fetch. Use when you need to inspect native-level HTTP traffic that is invisible to JS fetch interception. @@ -41,6 +41,9 @@ Fails if native devtools are not connected or the app is not running.`, }), async execute(services, params) { const device = resolveDevice(params.udid); + // Gate host deps per device kind: local sims need xcrun, remote sims route + // via sim-remote — a global `requires:["xcrun"]` would wrongly 424 a remote + // sim on an xcrun-less host, so the dep check lives here, not on the def. await ensureDeps(device.platform === "ios-remote" ? ["sim-remote"] : ["xcrun"]); const api = services.nativeDevtools as NativeDevtoolsApi; diff --git a/packages/tool-server/src/tools/native-devtools/native-user-interactable-view-at-point.ts b/packages/tool-server/src/tools/native-devtools/native-user-interactable-view-at-point.ts index c30ed7cf4..b886925e2 100644 --- a/packages/tool-server/src/tools/native-devtools/native-user-interactable-view-at-point.ts +++ b/packages/tool-server/src/tools/native-devtools/native-user-interactable-view-at-point.ts @@ -63,7 +63,7 @@ type Result = export const nativeUserInteractableViewAtPointTool: ToolDefinition = { id: "native-user-interactable-view-at-point", - capability: { apple: { simulator: true, device: true }, appleRemote: { simulator: true } }, + capability: { apple: { simulator: true, device: false }, appleRemote: { simulator: true } }, description: `Inspect the deepest UIView at a raw native window point that would actually receive touch input. Unlike native-view-at-point, this respects userInteractionEnabled and is closer to @@ -79,6 +79,9 @@ If status is restart_required: call restart-app then retry.`, }), async execute(services, params) { const device = resolveDevice(params.udid); + // Gate host deps per device kind: local sims need xcrun, remote sims route + // via sim-remote — a global `requires:["xcrun"]` would wrongly 424 a remote + // sim on an xcrun-less host, so the dep check lives here, not on the def. await ensureDeps(device.platform === "ios-remote" ? ["sim-remote"] : ["xcrun"]); const api = services.nativeDevtools as NativeDevtoolsApi; diff --git a/packages/tool-server/src/tools/native-devtools/native-view-at-point.ts b/packages/tool-server/src/tools/native-devtools/native-view-at-point.ts index f5f21a136..2eb607494 100644 --- a/packages/tool-server/src/tools/native-devtools/native-view-at-point.ts +++ b/packages/tool-server/src/tools/native-devtools/native-view-at-point.ts @@ -63,7 +63,7 @@ type Result = export const nativeViewAtPointTool: ToolDefinition = { id: "native-view-at-point", - capability: { apple: { simulator: true, device: true }, appleRemote: { simulator: true } }, + capability: { apple: { simulator: true, device: false }, appleRemote: { simulator: true } }, description: `Inspect the deepest visible UIView at a raw native window point. Unlike native-user-interactable-view-at-point, this ignores userInteractionEnabled, @@ -79,6 +79,9 @@ If status is restart_required: call restart-app then retry.`, }), async execute(services, params) { const device = resolveDevice(params.udid); + // Gate host deps per device kind: local sims need xcrun, remote sims route + // via sim-remote — a global `requires:["xcrun"]` would wrongly 424 a remote + // sim on an xcrun-less host, so the dep check lives here, not on the def. await ensureDeps(device.platform === "ios-remote" ? ["sim-remote"] : ["xcrun"]); const api = services.nativeDevtools as NativeDevtoolsApi; diff --git a/packages/tool-server/src/tools/open-url/index.ts b/packages/tool-server/src/tools/open-url/index.ts index ceecd1105..1a39fe564 100644 --- a/packages/tool-server/src/tools/open-url/index.ts +++ b/packages/tool-server/src/tools/open-url/index.ts @@ -1,8 +1,9 @@ import { z } from "zod"; import type { ServiceRef, ToolCapability, ToolDefinition } from "@argent/registry"; import { dispatchByPlatform } from "../../utils/cross-platform-tool"; -import { resolveDevice } from "../../utils/device-info"; +import { isPhysicalIos, resolveDevice } from "../../utils/device-info"; import { chromiumCdpRef } from "../../blueprints/chromium-cdp"; +import { physicalIosAutomationRef } from "../../blueprints/physical-ios-automation"; import type { OpenUrlResult, OpenUrlServices } from "./types"; import { iosImpl } from "./platforms/ios"; import { androidImpl } from "./platforms/android"; @@ -43,6 +44,9 @@ Returns { opened, url }. Fails if no app is registered to handle the URI (iOS/An if (device.platform === "chromium") { return { chromium: chromiumCdpRef(device) }; } + if (isPhysicalIos(device)) { + return { physicalIos: physicalIosAutomationRef(device) }; + } return {}; }, execute: dispatchByPlatform< diff --git a/packages/tool-server/src/tools/open-url/platforms/ios.ts b/packages/tool-server/src/tools/open-url/platforms/ios.ts index 0bed1b6bd..6a1ec1e82 100644 --- a/packages/tool-server/src/tools/open-url/platforms/ios.ts +++ b/packages/tool-server/src/tools/open-url/platforms/ios.ts @@ -2,13 +2,18 @@ import { execFile } from "node:child_process"; import { promisify } from "node:util"; import { FAILURE_CODES, FailureError, subprocessFailureMetadata } from "@argent/registry"; import type { PlatformImpl } from "../../../utils/cross-platform-tool"; +import type { PhysicalIosAutomationApi } from "../../../blueprints/physical-ios-automation"; import type { OpenUrlParams, OpenUrlResult, OpenUrlServices } from "../types"; const execFileAsync = promisify(execFile); export const iosImpl: PlatformImpl = { requires: ["xcrun"], - handler: async (_services, params) => { + handler: async (services, params, device) => { + if (device.kind === "device") { + await (services.physicalIos as PhysicalIosAutomationApi).openUrl(params.url); + return { opened: true, url: params.url }; + } try { await execFileAsync("xcrun", ["simctl", "openurl", params.udid, params.url]); } catch (err) { diff --git a/packages/tool-server/src/tools/open-url/types.ts b/packages/tool-server/src/tools/open-url/types.ts index 2a7112027..436e3b06b 100644 --- a/packages/tool-server/src/tools/open-url/types.ts +++ b/packages/tool-server/src/tools/open-url/types.ts @@ -8,4 +8,7 @@ export interface OpenUrlResult { url: string; } -export type OpenUrlServices = Record; +export interface OpenUrlServices { + physicalIos?: PhysicalIosAutomationApi; +} +import type { PhysicalIosAutomationApi } from "../../blueprints/physical-ios-automation"; diff --git a/packages/tool-server/src/tools/paste/index.ts b/packages/tool-server/src/tools/paste/index.ts index c0ec32e21..d02ca757d 100644 --- a/packages/tool-server/src/tools/paste/index.ts +++ b/packages/tool-server/src/tools/paste/index.ts @@ -1,11 +1,15 @@ import { z } from "zod"; -import type { ToolCapability, ToolDefinition } from "@argent/registry"; +import type { ServiceRef, ToolCapability, ToolDefinition } from "@argent/registry"; import { simulatorServerRef, type SimulatorServerApi } from "../../blueprints/simulator-server"; -import { resolveDevice } from "../../utils/device-info"; +import { + physicalIosAutomationRef, + type PhysicalIosAutomationApi, +} from "../../blueprints/physical-ios-automation"; +import { isPhysicalIos, resolveDevice } from "../../utils/device-info"; import { sendCommand } from "../../utils/simulator-client"; const zodSchema = z.object({ - udid: z.string().min(1).describe("iOS simulator UDID — paste is iOS-only."), + udid: z.string().min(1).describe("iOS simulator or physical-device UDID — paste is iOS-only."), text: z.string().describe("Text to paste into the focused field"), }); @@ -25,17 +29,24 @@ const capability: ToolCapability = { export const pasteTool: ToolDefinition = { id: "paste", - description: `Fill the focused field on the iOS simulator by pasting text (fastest text entry). + description: `Fill the focused field on an iOS simulator or physical iPhone by pasting text (fastest text entry). Use when you need to fill a text input with a long string faster than character-by-character typing. Returns { pasted: true }. Fails if no field is focused or the simulator server is not running. Tap the text field first to focus it, then call paste. If paste doesn't work for a particular field, use the keyboard tool instead.`, zodSchema, capability, - services: (params) => ({ - simulatorServer: simulatorServerRef(resolveDevice(params.udid)), - }), + services: (params): Record => { + const device = resolveDevice(params.udid); + return isPhysicalIos(device) + ? { physicalIos: physicalIosAutomationRef(device) } + : { simulatorServer: simulatorServerRef(device) }; + }, async execute(services, params) { + if (isPhysicalIos(resolveDevice(params.udid))) { + await (services.physicalIos as PhysicalIosAutomationApi).pasteText(params.text); + return { pasted: true }; + } const api = services.simulatorServer as SimulatorServerApi; sendCommand(api, { cmd: "paste", text: params.text }); return { pasted: true }; diff --git a/packages/tool-server/src/tools/profiler/native-profiler/native-profiler-analyze.ts b/packages/tool-server/src/tools/profiler/native-profiler/native-profiler-analyze.ts index 182fe1420..873e878f6 100644 --- a/packages/tool-server/src/tools/profiler/native-profiler/native-profiler-analyze.ts +++ b/packages/tool-server/src/tools/profiler/native-profiler/native-profiler-analyze.ts @@ -40,7 +40,7 @@ export const nativeProfilerAnalyzeTool: ToolDefinition< id: "native-profiler-analyze", capability, description: `Analyze exported native trace data and return an LLM-optimized markdown report. -iOS: parses CPU time profile, UI hangs, and memory leaks from the exported XML files. +iOS: parses every available export. Simulator recordings include CPU time profile, UI hangs, and memory leaks; physical-device Time Profiler recordings include app-filtered CPU and hang data when available. Android: queries the Perfetto .pftrace via the in-process Perfetto trace-processor engine for CPU hotspots, UI hangs with jank reason + main-thread state breakdown, GC annotation, and an RSS-growth weak signal. Returns a structured markdown report with severity indicators, tables, and actionable suggestions. After presenting the report, ask the user whether to investigate further (drill-down with diff --git a/packages/tool-server/src/tools/profiler/native-profiler/native-profiler-start.ts b/packages/tool-server/src/tools/profiler/native-profiler/native-profiler-start.ts index bdde7fdfb..36eabd66d 100644 --- a/packages/tool-server/src/tools/profiler/native-profiler/native-profiler-start.ts +++ b/packages/tool-server/src/tools/profiler/native-profiler/native-profiler-start.ts @@ -1,10 +1,14 @@ import { z } from "zod"; -import type { ToolDefinition } from "@argent/registry"; +import type { ServiceRef, ToolDefinition } from "@argent/registry"; import { nativeProfilerSessionRef, type NativeProfilerSessionApi, } from "../../../blueprints/native-profiler-session"; -import { resolveDevice } from "../../../utils/device-info"; +import { isPhysicalIos, resolveDevice } from "../../../utils/device-info"; +import { + physicalIosAutomationRef, + type PhysicalIosAutomationApi, +} from "../../../blueprints/physical-ios-automation"; import { assertSupported } from "../../../utils/capability"; import { ensureDeps } from "../../../utils/check-deps"; import { startNativeProfilerIos } from "./platforms/ios"; @@ -44,16 +48,22 @@ export const nativeProfilerStartTool: ToolDefinition< > = { id: "native-profiler-start", capability, - description: `Start native profiling on a booted device. iOS: Instruments via xctrace (CPU, hangs, memory). Android: Perfetto (CPU, jank, RSS-growth weak signal). + description: `Start native profiling on a booted device. iOS simulator: Instruments via xctrace (CPU, hangs, memory). Physical iPhone: device-wide Time Profiler capture filtered to the target app PID (CPU and available hang data, including protected system apps that reject direct attach). Android: Perfetto (CPU, jank, RSS-growth weak signal). Auto-detects the running app process unless app_process is explicitly provided. After starting, let the user interact with the app, then call native-profiler-stop. Use when you want to capture native CPU, hang, and memory data for a running app. Returns { status, pid, traceFile } confirming the recording has started. Fails if no app is running on the device, or the profiler cannot attach to the process.`, zodSchema, - services: (params) => ({ - session: nativeProfilerSessionRef(resolveDevice(params.device_id)), - }), + services: (params): Record => { + const device = resolveDevice(params.device_id); + return { + session: nativeProfilerSessionRef(device), + ...(!params.app_process && isPhysicalIos(device) + ? { physicalIos: physicalIosAutomationRef(device) } + : {}), + }; + }, async execute(services, params) { const api = services.session as NativeProfilerSessionApi; const device = resolveDevice(params.device_id); @@ -65,6 +75,13 @@ Fails if no app is running on the device, or the profiler cannot attach to the p // iOS-UDID regex. if (api.platform === "ios") { await ensureDeps(["xcrun"]); + if (!params.app_process && isPhysicalIos(device)) { + const active = await (services.physicalIos as PhysicalIosAutomationApi).activeApp(); + return startNativeProfilerIos(api, { + ...params, + active_physical_bundle_id: active.bundleId, + }); + } return startNativeProfilerIos(api, params); } await ensureDeps(["adb"]); diff --git a/packages/tool-server/src/tools/profiler/native-profiler/native-profiler-stop.ts b/packages/tool-server/src/tools/profiler/native-profiler/native-profiler-stop.ts index 6d7d87cdd..c1f1ca976 100644 --- a/packages/tool-server/src/tools/profiler/native-profiler/native-profiler-stop.ts +++ b/packages/tool-server/src/tools/profiler/native-profiler/native-profiler-stop.ts @@ -4,7 +4,11 @@ import { nativeProfilerSessionRef, type NativeProfilerSessionApi, } from "../../../blueprints/native-profiler-session"; -import { resolveDevice } from "../../../utils/device-info"; +import { isPhysicalIos, resolveDevice } from "../../../utils/device-info"; +import { + physicalIosAutomationRef, + type PhysicalIosAutomationApi, +} from "../../../blueprints/physical-ios-automation"; import { assertSupported } from "../../../utils/capability"; import { ensureDeps } from "../../../utils/check-deps"; import { stopNativeProfilerIos, type IosStopResult } from "./platforms/ios"; @@ -80,16 +84,22 @@ export const nativeProfilerStopTool: ToolDefinition, S id: "native-profiler-stop", capability, description: `Stop native profiling and export trace data. -iOS: sends SIGINT to xctrace, waits for packaging, then exports CPU, hangs, and leaks XML. +iOS: sends SIGINT to xctrace and waits for packaging. Simulator captures export CPU, hangs, and leaks XML; physical-device Time Profiler captures export app-filtered CPU and hang XML when that table is present. Android: sends SIGTERM to the perfetto daemon, polls /proc/, then \`adb pull\`s the .pftrace. Call native-profiler-start first. Use when the user has finished the interaction to profile and you need to export the trace. Returns { traceFile, exportedFiles, exportDiagnostics? }; traceFile is the raw trace bundle and exportedFiles the exports, all downloadable artifacts materialized to local paths. Fails if no active native-profiler-start session exists for the given device_id.`, zodSchema, - services: (params) => ({ - session: nativeProfilerSessionRef(resolveDevice(params.device_id)), - }), + services: (params) => { + const device = resolveDevice(params.device_id); + return { + session: nativeProfilerSessionRef(device), + ...(isPhysicalIos(device) + ? { physicalIos: physicalIosAutomationRef(device) } + : {}), + }; + }, async execute(services, params, ctx) { const api = services.session as NativeProfilerSessionApi; const device = resolveDevice(params.device_id); @@ -102,6 +112,7 @@ Fails if no active native-profiler-start session exists for the given device_id. // the "no active session" error path never needs it. if (api.platform === "ios") { await ensureDeps(["xcrun"]); + await (services.physicalIos as PhysicalIosAutomationApi | undefined)?.flushControls(); const ios: IosStopResult = await stopNativeProfilerIos(api); const artifacts = requireArtifacts(ctx); const result: IosStopArtifacts = { diff --git a/packages/tool-server/src/tools/profiler/native-profiler/platforms/ios.ts b/packages/tool-server/src/tools/profiler/native-profiler/platforms/ios.ts index c9698143d..add5ded3e 100644 --- a/packages/tool-server/src/tools/profiler/native-profiler/platforms/ios.ts +++ b/packages/tool-server/src/tools/profiler/native-profiler/platforms/ios.ts @@ -15,11 +15,17 @@ import { exportIosTraceData } from "../../../../utils/ios-profiler/export"; import type { ExportDiagnostics } from "../../../../utils/ios-profiler/export"; import { shutdownChild } from "../../../../utils/profiler-shared/lifecycle"; import { runIosProfilerPipeline } from "../../../../utils/ios-profiler/pipeline/index"; -import { selectIosCaptureStrategy } from "../../../../utils/ios-profiler/capture-strategy"; +import { + physicalAllProcessesStrategy, + selectIosCaptureStrategy, +} from "../../../../utils/ios-profiler/capture-strategy"; import type { NativeProfilerAnalyzeResult } from "../../../../utils/ios-profiler/types"; import { renderNativeProfilerReport } from "../../../../utils/ios-profiler/render"; import { formatTraceFreshness } from "../../../../utils/profiler-shared/freshness"; import { RECORDING_CAP_MS } from "../../../../utils/profiler-shared/types"; +import { isPhysicalIosUdid } from "../../../../utils/device-info"; +import { randomUUID } from "node:crypto"; +import { tmpdir } from "node:os"; // Two candidates because __dirname differs by runtime: bundled it's argent/dist/ // (template in argent/assets/); in dev it's tool-server/dist/tools/profiler/ @@ -307,6 +313,156 @@ export interface IosStartParams { device_id: string; app_process?: string; template_path?: string; + /** Internal: frontmost bundle id resolved through WDA for a physical device. */ + active_physical_bundle_id?: string; +} + +interface DevicectlApp { + bundleIdentifier: string; + url: string; +} + +interface DevicectlProcess { + processIdentifier: number; + executable: string; +} + +async function resolvePhysicalAppProcess( + udid: string, + bundleId: string +): Promise { + const appsPath = path.join(tmpdir(), `argent-profiler-apps-${randomUUID()}.json`); + const processesPath = path.join(tmpdir(), `argent-profiler-processes-${randomUUID()}.json`); + try { + execFileSync( + "xcrun", + [ + "devicectl", + "device", + "info", + "apps", + "--device", + udid, + "--include-all-apps", + "--quiet", + "--json-output", + appsPath, + ], + { timeout: DETECT_RUNNING_APP_TIMEOUT_MS, maxBuffer: DEFAULT_EXEC_MAX_BUFFER } + ); + execFileSync( + "xcrun", + [ + "devicectl", + "device", + "info", + "processes", + "--device", + udid, + "--quiet", + "--json-output", + processesPath, + ], + { timeout: DETECT_RUNNING_APP_TIMEOUT_MS, maxBuffer: DEFAULT_EXEC_MAX_BUFFER } + ); + const apps = JSON.parse(await fs.readFile(appsPath, "utf8")) as { + result?: { apps?: DevicectlApp[] }; + }; + const processes = JSON.parse(await fs.readFile(processesPath, "utf8")) as { + result?: { runningProcesses?: DevicectlProcess[] }; + }; + const app = apps.result?.apps?.find((item) => item.bundleIdentifier === bundleId); + if (!app?.url) { + throw new Error(`devicectl did not return an installed app for ${bundleId}`); + } + const appUrl = app.url.endsWith("/") ? app.url : `${app.url}/`; + const process = processes.result?.runningProcesses?.find( + (item) => item.executable.startsWith(appUrl) && !item.executable.includes(".appex/") + ); + if (!process) { + throw new Error(`no running process matched the frontmost app ${bundleId}`); + } + const executable = decodeURIComponent(path.basename(new URL(process.executable).pathname)); + return { executable, pid: process.processIdentifier }; + } catch (err) { + throw new FailureError( + `Could not resolve the frontmost physical-device app process for ${bundleId}. ` + + `Keep the device unlocked and the app in the foreground, or pass app_process explicitly.`, + { + error_code: FAILURE_CODES.NATIVE_PROFILER_APP_PROCESS_LIST_FAILED, + failure_stage: "native_profiler_detect_physical_process", + failure_area: "tool_server", + error_kind: "subprocess", + ...subprocessFailureMetadata(err, "xcrun_devicectl"), + }, + { cause: err instanceof Error ? err : new Error(String(err)) } + ); + } finally { + await Promise.all([ + fs.rm(appsPath, { force: true }).catch(() => {}), + fs.rm(processesPath, { force: true }).catch(() => {}), + ]); + } +} + +async function resolvePhysicalProcessByName( + udid: string, + requestedName: string +): Promise { + const processesPath = path.join( + tmpdir(), + `argent-profiler-processes-${randomUUID()}.json` + ); + try { + execFileSync( + "xcrun", + [ + "devicectl", + "device", + "info", + "processes", + "--device", + udid, + "--quiet", + "--json-output", + processesPath, + ], + { timeout: DETECT_RUNNING_APP_TIMEOUT_MS, maxBuffer: DEFAULT_EXEC_MAX_BUFFER } + ); + const parsed = JSON.parse(await fs.readFile(processesPath, "utf8")) as { + result?: { runningProcesses?: DevicectlProcess[] }; + }; + const requested = requestedName.toLocaleLowerCase(); + const process = parsed.result?.runningProcesses?.find((item) => { + const executable = decodeURIComponent(path.basename(new URL(item.executable).pathname)); + return ( + executable.toLocaleLowerCase() === requested || + String(item.processIdentifier) === requestedName + ); + }); + if (!process) { + throw new Error(`no running physical-device process matched ${requestedName}`); + } + return { + executable: decodeURIComponent(path.basename(new URL(process.executable).pathname)), + pid: process.processIdentifier, + }; + } catch (err) { + throw new FailureError( + `Could not resolve running physical-device process "${requestedName}". ` + + `Launch it first or omit app_process to profile the frontmost app.`, + { + error_code: FAILURE_CODES.NATIVE_PROFILER_APP_PROCESS_LIST_FAILED, + failure_stage: "native_profiler_detect_physical_process", + failure_area: "tool_server", + error_kind: "subprocess", + ...subprocessFailureMetadata(err, "xcrun_devicectl"), + }, + { cause: err instanceof Error ? err : new Error(String(err)) } + ); + } finally { + await fs.rm(processesPath, { force: true }).catch(() => {}); + } } export async function startNativeProfilerIos( @@ -325,20 +481,29 @@ export async function startNativeProfilerIos( ); } - const templatePath = params.template_path ?? resolveDefaultTemplatePath(); + const physical = isPhysicalIosUdid(params.device_id); + const templatePath = + params.template_path ?? (physical ? "Time Profiler" : resolveDefaultTemplatePath()); const detected = params.app_process - ? resolveExplicitApp(params.device_id, params.app_process) - : detectRunningApp(params.device_id); + ? physical + ? await resolvePhysicalProcessByName(params.device_id, params.app_process) + : resolveExplicitApp(params.device_id, params.app_process) + : physical + ? await resolvePhysicalAppProcess( + params.device_id, + params.active_physical_bundle_id ?? "" + ) + : detectRunningApp(params.device_id); const appProcess = detected.executable; // Pick the capture approach for this environment. On Xcode versions where // `xctrace --device` works this is the original device/attach path; on the // 26.4–27.0 regression (where --device deadlocks) it is the host-wide // --all-processes fallback, filtered to the app PID. See capture-strategy. - const strategy = selectIosCaptureStrategy(); + const strategy = physical ? physicalAllProcessesStrategy : selectIosCaptureStrategy(); // The all-processes fallback records host-wide and isolates the app by PID, so // it can only run when the target is actually running (PID known). - if (strategy.name === "all-processes" && detected.pid == null) { + if (!strategy.attachesByName && detected.pid == null) { throw new FailureError( `The all-processes capture fallback needs the target app to be running so its ` + `samples can be isolated by PID, but no running PID was found for "${appProcess}". ` + @@ -480,6 +645,12 @@ export interface IosStopResult { warning?: string; } +function exportTraceForDevice(api: NativeProfilerSessionApi, traceFile: string) { + return isPhysicalIosUdid(api.deviceId) + ? exportIosTraceData(traceFile, { tolerateMissingHangs: true }) + : exportIosTraceData(traceFile); +} + export async function stopNativeProfilerIos(api: NativeProfilerSessionApi): Promise { if ((api.recordingTimedOut || api.recordingExitedUnexpectedly) && api.traceFile) { const traceFile = api.traceFile; @@ -489,7 +660,7 @@ export async function stopNativeProfilerIos(api: NativeProfilerSessionApi): Prom api.recordingExitedUnexpectedly = false; api.lastExitInfo = null; - const { files: exportedFiles, diagnostics } = await exportIosTraceData(traceFile); + const { files: exportedFiles, diagnostics } = await exportTraceForDevice(api, traceFile); api.exportedFiles = exportedFiles; const warning = wasTimeout @@ -544,7 +715,7 @@ export async function stopNativeProfilerIos(api: NativeProfilerSessionApi): Prom api.recordingExitedUnexpectedly = false; api.lastExitInfo = null; - const { files: exportedFiles, diagnostics } = await exportIosTraceData(api.traceFile); + const { files: exportedFiles, diagnostics } = await exportTraceForDevice(api, api.traceFile); api.exportedFiles = exportedFiles; const stopResult: IosStopResult = { diff --git a/packages/tool-server/src/tools/reinstall-app/platforms/ios.ts b/packages/tool-server/src/tools/reinstall-app/platforms/ios.ts index 4d81f91be..7917d7f4a 100644 --- a/packages/tool-server/src/tools/reinstall-app/platforms/ios.ts +++ b/packages/tool-server/src/tools/reinstall-app/platforms/ios.ts @@ -9,7 +9,47 @@ const execFileAsync = promisify(execFile); export const iosImpl: PlatformImpl = { requires: ["xcrun"], - handler: async (_services, params) => { + handler: async (_services, params, device) => { + if (device.kind === "device") { + const absolute = resolvePath(params.appPath); + try { + await execFileAsync("xcrun", [ + "devicectl", + "device", + "uninstall", + "app", + "--device", + params.udid, + params.bundleId, + ]); + } catch { + // App may not be installed — continue to install. + } + try { + await execFileAsync("xcrun", [ + "devicectl", + "device", + "install", + "app", + "--device", + params.udid, + absolute, + ]); + } catch (err) { + throw new FailureError( + `Failed to install signed iOS app bundle on physical device ${params.udid}.`, + { + error_code: FAILURE_CODES.IOS_REINSTALL_INSTALL_FAILED, + failure_stage: "ios_reinstall_app_devicectl_install", + failure_area: "tool_server", + error_kind: "subprocess", + ...subprocessFailureMetadata(err, "xcrun_devicectl"), + }, + { cause: err instanceof Error ? err : new Error(String(err)) } + ); + } + return { reinstalled: true, bundleId: params.bundleId }; + } const { udid, bundleId, appPath } = params; const absolute = resolvePath(appPath); try { diff --git a/packages/tool-server/src/tools/restart-app/platforms/ios.ts b/packages/tool-server/src/tools/restart-app/platforms/ios.ts index 3d5b86bdc..8351e94f0 100644 --- a/packages/tool-server/src/tools/restart-app/platforms/ios.ts +++ b/packages/tool-server/src/tools/restart-app/platforms/ios.ts @@ -12,6 +12,10 @@ import { type NativeDevtoolsApi, } from "../../../blueprints/native-devtools"; import type { PlatformImpl } from "../../../utils/cross-platform-tool"; +import { + physicalIosAutomationRef, + type PhysicalIosAutomationApi, +} from "../../../blueprints/physical-ios-automation"; import type { RestartAppParams, RestartAppResult } from "../types"; const execFileAsync = promisify(execFile); @@ -27,6 +31,16 @@ export function makeIosImpl( requires: ["xcrun"], handler: async (_services, params, device) => { const { udid, bundleId } = params; + if (device.kind === "device") { + const ref = physicalIosAutomationRef(device); + const physicalIos = await registry.resolveService( + ref.urn, + ref.options + ); + await physicalIos.terminateApp(bundleId).catch(() => false); + await physicalIos.launchApp(bundleId); + return { restarted: true, bundleId }; + } const ndRef = nativeDevtoolsRef(device); const nativeDevtools = await registry.resolveService( ndRef.urn, diff --git a/packages/tool-server/src/tools/rotate/index.ts b/packages/tool-server/src/tools/rotate/index.ts index 840f8f00a..c452178af 100644 --- a/packages/tool-server/src/tools/rotate/index.ts +++ b/packages/tool-server/src/tools/rotate/index.ts @@ -1,7 +1,11 @@ import { z } from "zod"; -import type { ToolCapability, ToolDefinition } from "@argent/registry"; +import type { ServiceRef, ToolCapability, ToolDefinition } from "@argent/registry"; import { simulatorServerRef, type SimulatorServerApi } from "../../blueprints/simulator-server"; -import { resolveDevice } from "../../utils/device-info"; +import { + physicalIosAutomationRef, + type PhysicalIosAutomationApi, +} from "../../blueprints/physical-ios-automation"; +import { isPhysicalIos, resolveDevice } from "../../utils/device-info"; import { sendCommand } from "../../utils/simulator-client"; const zodSchema = z.object({ @@ -30,10 +34,17 @@ Use to test layout in a different orientation. Re-run \`describe\` afterwards Returns { orientation }. Fails if the target device is not booted.`, zodSchema, capability, - services: (params) => ({ - simulatorServer: simulatorServerRef(resolveDevice(params.udid)), - }), + services: (params): Record => { + const device = resolveDevice(params.udid); + return isPhysicalIos(device) + ? { physicalIos: physicalIosAutomationRef(device) } + : { simulatorServer: simulatorServerRef(device) }; + }, async execute(services, params) { + if (isPhysicalIos(resolveDevice(params.udid))) { + await (services.physicalIos as PhysicalIosAutomationApi).rotate(params.orientation); + return { orientation: params.orientation }; + } const api = services.simulatorServer as SimulatorServerApi; sendCommand(api, { cmd: "rotate", direction: params.orientation }); return { orientation: params.orientation }; diff --git a/packages/tool-server/src/tools/run-sequence/index.ts b/packages/tool-server/src/tools/run-sequence/index.ts index 4502a2073..e0a4e2823 100644 --- a/packages/tool-server/src/tools/run-sequence/index.ts +++ b/packages/tool-server/src/tools/run-sequence/index.ts @@ -145,13 +145,15 @@ Stops on the first error (or unmet await-ui-element condition) and returns parti capability, // No eagerly-declared service: each step resolves its own services through // `invokeSubTool` below (simulator-server for iOS/Android, CDP for - // Chromium), so run-sequence itself needs none. An eager resolver can't be - // used here because a tvOS udid shape-classifies as `ios` (there is no - // `tvos` platform) — declaring simulator-server for it would spawn a - // controller it can't drive and hang on the ready timeout before any tv-* - // step could run. The sub-tool invocations still pay only their own - // first-step spawn cost, and `ctx` is threaded through so nested steps keep - // the outer request's telemetry attribution. + // Chromium, WebDriverAgent for physical iOS), so run-sequence itself needs + // none. An eager resolver can't be used here because a tvOS udid + // shape-classifies as `ios` (there is no `tvos` platform) — declaring + // simulator-server for it would spawn a controller it can't drive and + // hang on the ready timeout before any tv-* step could run; a physical + // iOS udid hits the same problem, since simulator-server's guard throws + // for kind === "device" before step 1 even runs. The sub-tool invocations + // still pay only their own first-step spawn cost, and `ctx` is threaded + // through so nested steps keep the outer request's telemetry attribution. services: () => ({}), async execute(_services, params, ctx?: ToolContext) { const { udid, steps } = params; diff --git a/packages/tool-server/src/tools/screenshot-diff/index.ts b/packages/tool-server/src/tools/screenshot-diff/index.ts index 974cbaa5f..e11aaa66d 100644 --- a/packages/tool-server/src/tools/screenshot-diff/index.ts +++ b/packages/tool-server/src/tools/screenshot-diff/index.ts @@ -12,7 +12,11 @@ import type { ToolDefinition, } from "@argent/registry"; import { simulatorServerRef, type SimulatorServerApi } from "../../blueprints/simulator-server"; -import { resolveDevice } from "../../utils/device-info"; +import { + physicalIosAutomationRef, + type PhysicalIosAutomationApi, +} from "../../blueprints/physical-ios-automation"; +import { isPhysicalIos, resolveDevice } from "../../utils/device-info"; import { httpScreenshot } from "../../utils/simulator-client"; import { requireArtifacts, type ArtifactHandle } from "../../artifacts"; import { diffPngFiles } from "./screenshot-diff"; @@ -112,7 +116,10 @@ Fails if the input sources are invalid, PNG files cannot be read, outputDir cann // for pure static-PNG diffs, which fails on tvOS simulators that have no // SimulatorServer backend. if (params.captureBaseline || params.captureCurrent) { - return { simulatorServer: simulatorServerRef(resolveDevice(params.udid)) }; + const device = resolveDevice(params.udid); + return isPhysicalIos(device) + ? { physicalIos: physicalIosAutomationRef(device) } + : { simulatorServer: simulatorServerRef(device) }; } return {}; }, @@ -191,7 +198,8 @@ async function resolveInputPaths( const baselinePath = params.captureBaseline ? await captureLiveInput({ - api: requireSimulatorServer(services), + api: services.physicalIos ? undefined : requireSimulatorServer(services), + physicalIos: services.physicalIos as PhysicalIosAutomationApi | undefined, outputDir, name: "baseline", rotation: params.rotation, @@ -202,7 +210,8 @@ async function resolveInputPaths( const currentPath = params.captureCurrent ? await captureLiveInput({ - api: requireSimulatorServer(services), + api: services.physicalIos ? undefined : requireSimulatorServer(services), + physicalIos: services.physicalIos as PhysicalIosAutomationApi | undefined, outputDir, name: "current", rotation: params.rotation, @@ -273,7 +282,8 @@ function requireSimulatorServer(services: Record): SimulatorSer async function captureLiveInput(params: { // Resolved and validated by requireSimulatorServer at the call site, so it is // never undefined here. - api: SimulatorServerApi; + api?: SimulatorServerApi; + physicalIos?: PhysicalIosAutomationApi; outputDir: string; name: "baseline" | "current"; rotation?: Params["rotation"]; @@ -288,10 +298,18 @@ async function captureLiveInput(params: { // normalization in diffPngFiles keeps a scaled capture diff-compatible with a // baseline saved at any scale. Full-res is preserved wherever it works (iOS). let capture: Awaited>; - try { - capture = await params.captureScreenshot(params.api, params.rotation, params.signal, 1.0); - } catch { - capture = await params.captureScreenshot(params.api, params.rotation, params.signal); + if (params.physicalIos) { + if (params.rotation) await params.physicalIos.rotate(params.rotation); + await params.physicalIos.flushControls(); + const physicalCapture = await params.physicalIos.screenshot(); + capture = { ...physicalCapture, url: "" }; + } else { + if (!params.api) throw new Error("Live screenshot capture requires a device service."); + try { + capture = await params.captureScreenshot(params.api, params.rotation, params.signal, 1.0); + } catch { + capture = await params.captureScreenshot(params.api, params.rotation, params.signal); + } } const suffix = crypto.randomBytes(4).toString("hex"); const destination = path.join(params.outputDir, `${params.name}-${suffix}.live.png`); diff --git a/packages/tool-server/src/tools/screenshot/index.ts b/packages/tool-server/src/tools/screenshot/index.ts index f95e92391..3d5cd2030 100644 --- a/packages/tool-server/src/tools/screenshot/index.ts +++ b/packages/tool-server/src/tools/screenshot/index.ts @@ -6,7 +6,11 @@ import { z } from "zod"; import type { Registry, ToolCapability, ToolDefinition } from "@argent/registry"; import { simulatorServerRef, type SimulatorServerApi } from "../../blueprints/simulator-server"; import { chromiumCdpRef, type ChromiumCdpApi } from "../../blueprints/chromium-cdp"; -import { resolveDevice } from "../../utils/device-info"; +import { + physicalIosAutomationRef, + type PhysicalIosAutomationApi, +} from "../../blueprints/physical-ios-automation"; +import { resolveDevice, isPhysicalIos } from "../../utils/device-info"; import { getScreenshotScale, httpScreenshot } from "../../utils/simulator-client"; import { isTvOsSimulator } from "../../utils/ios-devices"; import { captureVegaScreenshotPng } from "../../utils/vega-screen"; @@ -120,11 +124,12 @@ export async function tvTargetLongSide(file: string, scale: number): Promise { return { id: "screenshot", - description: `Capture a screenshot of the device screen (iOS simulator, Android emulator, Apple TV simulator, Vega, or Chromium app). Returns { image }; the MCP adapter renders it as a visible image unless the caller passed includeImageInContext: false. + description: `Capture a screenshot of the device screen (iOS simulator, physical iPhone, Android emulator, Apple TV simulator, Vega, or Chromium app). Returns { image }; the MCP adapter renders it as a visible image unless the caller passed includeImageInContext: false. Use when you need a baseline image before an interaction or to inspect the current screen state after a delay. Fails if the simulator-server / emulator backend / Chromium CDP is not reachable for the given device.`, alwaysLoad: true, - searchHint: "device simulator emulator chromium screen image capture baseline tvos apple tv", + searchHint: + "device simulator emulator chromium screen image capture baseline tvos apple tv vega fire tv", zodSchema, outputHint: "image", capability, @@ -150,6 +155,22 @@ Fails if the simulator-server / emulator backend / Chromium CDP is not reachable return { image }; } + // A physical iOS device captures over WebDriverAgent, not simulator-server. + // WDA returns a full-resolution PNG; + // rotation/scale/downscaler are simulator/Chromium-only knobs and don't + // apply to the device. Checked before the (async, simulator-only) tvOS + // probe below since a physical udid isn't a simulator runtime at all. + if (isPhysicalIos(device)) { + const ref = physicalIosAutomationRef(device); + const physicalIos = (await registry.resolveService( + ref.urn, + ref.options + )) as PhysicalIosAutomationApi; + const { path: capturedPath } = await physicalIos.screenshot(); + const image = await requireArtifacts(ctx).register(capturedPath, { mimeType: "image/png" }); + return { image }; + } + // Distinguish tvOS from iOS by simulator runtime — shape alone can't. // tvOS has no simulator-server backend, so capture via xcrun instead. if (device.platform === "ios" && (await isTvOsSimulator(params.udid))) { diff --git a/packages/tool-server/src/utils/device-info.ts b/packages/tool-server/src/utils/device-info.ts index 89994d38a..0ef68277d 100644 --- a/packages/tool-server/src/utils/device-info.ts +++ b/packages/tool-server/src/utils/device-info.ts @@ -11,6 +11,17 @@ import type { DeviceInfo, DeviceKind, Platform } from "@argent/registry"; const IOS_UDID_SHAPE = /^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$/; +/** + * Physical iPhone/iPad UDID shape on Apple silicon devices (A12+/iOS 17+): + * an 8-hex ECID prefix, a single dash, then 16 hex — e.g. + * `00008120-000E6D0C0ABBA01E`. This is distinct from the simulator UUID + * (four dashes) so a real device can be told apart from a simulator by shape + * alone, the same way Android emulators vs phones are distinguished. Older + * 40-hex device UDIDs belong to pre-A12 hardware outside the iOS 17+ full-parity + * target for this backend, so they are intentionally not matched. + */ +const IOS_PHYSICAL_UDID_SHAPE = /^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{16}$/; + /** * Prefix used on device ids that route through `sim-remote` to a remote iOS * simulator. The raw UUID after the prefix is the same RFC-4122 shape as a @@ -31,6 +42,11 @@ export function withRemotePrefix(udid: string): string { export const CHROMIUM_ID_PREFIX = "chromium-cdp-"; +/** Whether a udid is a physical iOS device (vs a simulator UUID), by shape. */ +export function isPhysicalIosUdid(udid: string): boolean { + return IOS_PHYSICAL_UDID_SHAPE.test(udid); +} + /** * Vega serial prefix. `vega device list` reports VVD / Fire-TV serials as * `amazon-` (e.g. `amazon-4a27df03c9777152`). No *known* Android adb serial @@ -49,7 +65,8 @@ export function classifyDevice(udid: string): Platform { if (udid.startsWith(REMOTE_PREFIX)) return "ios-remote"; if (udid.startsWith(VEGA_SERIAL_PREFIX)) return "vega"; if (udid.startsWith(CHROMIUM_ID_PREFIX)) return "chromium"; - return IOS_UDID_SHAPE.test(udid) ? "ios" : "android"; + if (IOS_UDID_SHAPE.test(udid) || IOS_PHYSICAL_UDID_SHAPE.test(udid)) return "ios"; + return "android"; } /** @@ -72,8 +89,9 @@ export function isAndroidEmulatorSerial(serial: string): boolean { /** * Build a `DeviceInfo` from a raw udid, by shape. Kind defaults per platform: - * 'simulator' for iOS / ios-remote, 'vvd' for Vega, 'emulator'/'device' for - * Android by serial shape, 'app' for Chromium — platform impls can enrich with + * 'simulator' for an iOS simulator or ios-remote ('device' for a physical + * iPhone/iPad by UDID shape), 'vvd' for Vega, 'emulator'/'device' for Android + * by serial shape, 'app' for Chromium — platform impls can enrich with * name/state/sdkLevel via simctl/adb/sim-remote if needed. * * Vega is VVD-only in v1: the tool-server does not connect to or detect physical @@ -86,18 +104,27 @@ export function isAndroidEmulatorSerial(serial: string): boolean { export function resolveDevice(udid: string): DeviceInfo { const platform = classifyDevice(udid); const kind: DeviceKind = - platform === "ios" || platform === "ios-remote" - ? "simulator" - : platform === "vega" - ? "vvd" - : platform === "android" - ? isAndroidEmulatorSerial(udid) - ? "emulator" - : "device" - : "app"; + platform === "ios" + ? isPhysicalIosUdid(udid) + ? "device" + : "simulator" + : platform === "ios-remote" + ? "simulator" + : platform === "vega" + ? "vvd" + : platform === "android" + ? isAndroidEmulatorSerial(udid) + ? "emulator" + : "device" + : "app"; return { id: udid, platform, kind }; } +/** A physical iOS device (driven via WebDriverAgent/XCTest, not simulator-server). */ +export function isPhysicalIos(device: DeviceInfo): boolean { + return device.platform === "ios" && device.kind === "device"; +} + /** Parses the CDP port out of a chromium device id. Returns null if the id is malformed. */ export function parseChromiumCdpPort(udid: string): number | null { if (!udid.startsWith(CHROMIUM_ID_PREFIX)) return null; diff --git a/packages/tool-server/src/utils/ios-devices.ts b/packages/tool-server/src/utils/ios-devices.ts index a2879af33..1c67c0692 100644 --- a/packages/tool-server/src/utils/ios-devices.ts +++ b/packages/tool-server/src/utils/ios-devices.ts @@ -1,5 +1,10 @@ import { execFile } from "node:child_process"; import { promisify } from "node:util"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { readFile, rm } from "node:fs/promises"; +import { randomUUID } from "node:crypto"; +import { isPhysicalIosUdid } from "./device-info"; const execFileAsync = promisify(execFile); @@ -11,6 +16,25 @@ export interface IosSimulator { runtimeKind?: "mobile" | "tv"; } +export interface IosPhysicalDevice { + udid: string; + name: string; + /** Apple product type, e.g. "iPhone15,4". Null when devicectl omits it. */ + productType: string | null; + /** Always "connected" — only currently-reachable devices are returned. */ + state: string; +} + +interface DevicectlDevice { + hardwareProperties?: { udid?: string; platform?: string; productType?: string }; + deviceProperties?: { name?: string }; + connectionProperties?: { transportType?: string; tunnelState?: string }; +} + +interface DevicectlOutput { + result?: { devices?: DevicectlDevice[] }; +} + interface SimctlDevice { udid: string; name: string; @@ -56,6 +80,63 @@ export async function listIosSimulators(): Promise { } } +/** + * List connected physical iOS devices via `xcrun devicectl list devices`. + * + * devicectl only emits stable machine output to a file (`--json-output`), never + * stdout, so we write to a temp file and parse it. We keep only devices that are + * actually reachable right now: iOS platform with a `connectionProperties.transportType` + * (wired/network). Paired-but-offline devices carry a `tunnelState: "unavailable"` + * and no `transportType`, and are dropped — listing them would invite taps that + * can't land. Returns an empty array on any failure so the rest of `list-devices` + * stays usable on non-mac hosts or without Xcode. + */ +export function parsePhysicalIosDevices(data: DevicectlOutput): IosPhysicalDevice[] { + const out: IosPhysicalDevice[] = []; + for (const d of data.result?.devices ?? []) { + const udid = d.hardwareProperties?.udid; + const platform = d.hardwareProperties?.platform; + const transport = d.connectionProperties?.transportType; + // Keep only iOS (skip watchOS/tvOS), with a physical ECID UDID, that is + // currently reachable. The `isPhysicalIosUdid` (8hex-16hex) check is + // load-bearing: `devicectl list devices` also enumerates the host's iOS + // *simulators*, which report `platform: "iOS"` with + // `transportType: "sameMachine"` (verified against real devicectl JSON) — + // without the shape gate every simulator surfaces as a phantom physical + // device. It also keeps discovery consistent with `classifyDevice`, which + // routes only this UDID shape to the CoreDevice backend. A reachable device + // reports a `transportType` (wired/network); paired-but-offline ones carry + // `tunnelState: "unavailable"` and no transport, and are dropped. + if (!udid || platform !== "iOS" || !isPhysicalIosUdid(udid) || !transport) continue; + if (d.connectionProperties?.tunnelState === "unavailable") continue; + out.push({ + udid, + name: d.deviceProperties?.name ?? "iPhone", + productType: d.hardwareProperties?.productType ?? null, + state: "connected", + }); + } + return out; +} + +export async function listIosDevices(): Promise { + if (process.platform !== "darwin") return []; + const outPath = join(tmpdir(), `argent-devicectl-${randomUUID()}.json`); + try { + await execFileAsync( + "xcrun", + ["devicectl", "list", "devices", "--quiet", "--json-output", outPath], + { timeout: 15_000 } + ); + const data: DevicectlOutput = JSON.parse(await readFile(outPath, "utf8")); + return parsePhysicalIosDevices(data); + } catch { + return []; + } finally { + await rm(outPath, { force: true }).catch(() => {}); + } +} + // A simulator's runtime kind is fixed at creation (an iOS sim can't become a // tvOS one), so memoize per-UDID to keep the hot describe/screenshot path from // paying the ~100ms `simctl list` cost on every call. Only successful lookups diff --git a/packages/tool-server/src/utils/ios-profiler/capture-strategy/index.ts b/packages/tool-server/src/utils/ios-profiler/capture-strategy/index.ts index f92aa8596..53c205559 100644 --- a/packages/tool-server/src/utils/ios-profiler/capture-strategy/index.ts +++ b/packages/tool-server/src/utils/ios-profiler/capture-strategy/index.ts @@ -1,4 +1,5 @@ export type { IosCaptureStrategy, CaptureTarget, RecordArgsInput } from "./types"; export { deviceStrategy } from "./device"; +export { physicalAllProcessesStrategy } from "./physical-all-processes"; export { allProcessesStrategy } from "./all-processes"; export { selectIosCaptureStrategy } from "./select"; diff --git a/packages/tool-server/src/utils/ios-profiler/capture-strategy/physical-all-processes.ts b/packages/tool-server/src/utils/ios-profiler/capture-strategy/physical-all-processes.ts new file mode 100644 index 000000000..c72064427 --- /dev/null +++ b/packages/tool-server/src/utils/ios-profiler/capture-strategy/physical-all-processes.ts @@ -0,0 +1,37 @@ +import type { CaptureTarget, IosCaptureStrategy, RecordArgsInput } from "./types"; + +/** + * Physical iOS system apps (Maps, Settings, Safari, …) reject a process-scoped + * Instruments attach because the host cannot acquire their task port. Device- + * wide Time Profiler capture is permitted and records CPU samples and potential + * hangs for every process. The analysis pipeline filters those rows to the PID + * resolved through devicectl, giving the same per-app report without requiring + * get-task-allow or ownership of the app. + */ +export const physicalAllProcessesStrategy: IosCaptureStrategy = { + name: "physical-all-processes", + description: "xctrace --device --all-processes, filtered to the app PID", + attachesByName: false, + + buildRecordArgs(input: RecordArgsInput): string[] { + const args = [ + "record", + "--template", + input.templatePath, + "--device", + input.deviceId, + "--all-processes", + "--output", + input.outputFile, + "--no-prompt", + ]; + if (input.notifyName) { + args.push("--notify-tracing-started", input.notifyName); + } + return args; + }, + + cpuFilterPid(target: CaptureTarget): number | null { + return target.pid; + }, +}; diff --git a/packages/tool-server/src/utils/ios-profiler/capture-strategy/types.ts b/packages/tool-server/src/utils/ios-profiler/capture-strategy/types.ts index ff32b1eaf..eda2199cc 100644 --- a/packages/tool-server/src/utils/ios-profiler/capture-strategy/types.ts +++ b/packages/tool-server/src/utils/ios-profiler/capture-strategy/types.ts @@ -38,7 +38,7 @@ export interface RecordArgsInput { export interface IosCaptureStrategy { /** Stable identifier — also the value accepted by the ARGENT_IOS_CAPTURE override. */ - readonly name: "device" | "all-processes"; + readonly name: "device" | "all-processes" | "physical-all-processes"; /** One-line human-readable description for logs. */ readonly description: string; /** Build the `xctrace record …` argv for this strategy. */ diff --git a/packages/tool-server/src/utils/ios-profiler/export.ts b/packages/tool-server/src/utils/ios-profiler/export.ts index 291db03dd..5bece8246 100644 --- a/packages/tool-server/src/utils/ios-profiler/export.ts +++ b/packages/tool-server/src/utils/ios-profiler/export.ts @@ -1,4 +1,5 @@ import * as path from "path"; +import { writeFile } from "node:fs/promises"; import { execFileAsyncWithTimeout } from "./run-with-timeout"; /** @@ -123,7 +124,10 @@ async function tryCpuExportFallback( return false; } -export async function exportIosTraceData(traceFile: string): Promise<{ +export async function exportIosTraceData( + traceFile: string, + options: { tolerateMissingHangs?: boolean } = {} +): Promise<{ files: Record; diagnostics: ExportDiagnostics; }> { @@ -194,6 +198,14 @@ export async function exportIosTraceData(traceFile: string): Promise<{ ]); exportedFiles[key] = outPath; } catch (err) { + if (key === "hangs" && options.tolerateMissingHangs) { + // Device-wide Time Profiler omits the potential-hangs node entirely + // when no hang occurred, and xctrace reports that as a failed xpath. + // It is a valid zero-result capture, not an analysis failure. + await writeFile(outPath, "\n", "utf8"); + exportedFiles[key] = outPath; + continue; + } const msg = err instanceof Error ? err.message : String(err); diagnostics.errors[key] = msg; exportedFiles[key] = null; diff --git a/packages/tool-server/src/utils/setup-registry.ts b/packages/tool-server/src/utils/setup-registry.ts index 32a8253b9..4891b6d89 100644 --- a/packages/tool-server/src/utils/setup-registry.ts +++ b/packages/tool-server/src/utils/setup-registry.ts @@ -1,6 +1,9 @@ import { Registry } from "@argent/registry"; import { isFlagEnabled } from "@argent/configuration-core"; import { simulatorServerBlueprint } from "../blueprints/simulator-server"; +import { coreDeviceBlueprint } from "../blueprints/core-device"; +import { physicalIosAutomationBlueprint } from "../blueprints/physical-ios-automation"; +import { deviceLogSessionBlueprint } from "../blueprints/device-log-session"; import { nativeDevtoolsBlueprint } from "../blueprints/native-devtools"; import { androidDevtoolsBlueprint } from "../blueprints/android-devtools"; import { axServiceBlueprint } from "../blueprints/ax-service"; @@ -61,6 +64,8 @@ import { nativeProfilerStartTool } from "../tools/profiler/native-profiler/nativ import { nativeProfilerStopTool } from "../tools/profiler/native-profiler/native-profiler-stop"; import { nativeProfilerAnalyzeTool } from "../tools/profiler/native-profiler/native-profiler-analyze"; import { nativeProfilerSessionBlueprint } from "../blueprints/native-profiler-session"; +import { deviceLogsStartTool } from "../tools/device-logs/device-logs-start"; +import { deviceLogsStopTool } from "../tools/device-logs/device-logs-stop"; import { profilerCpuQueryTool } from "../tools/profiler/query/profiler-cpu-query"; import { profilerCommitQueryTool } from "../tools/profiler/query/profiler-commit-query"; import { profilerStackQueryTool } from "../tools/profiler/query/profiler-stack-query"; @@ -93,6 +98,9 @@ export function createRegistry(): Registry { const registry = new Registry({ isFlagEnabled: (flag) => isFlagEnabled(flag) }); registry.registerBlueprint(simulatorServerBlueprint); + registry.registerBlueprint(coreDeviceBlueprint); + registry.registerBlueprint(physicalIosAutomationBlueprint); + registry.registerBlueprint(deviceLogSessionBlueprint); registry.registerBlueprint(jsRuntimeDebuggerBlueprint); registry.registerBlueprint(networkInspectorBlueprint); registry.registerBlueprint(reactProfilerSessionBlueprint); @@ -151,6 +159,8 @@ export function createRegistry(): Registry { registry.registerTool(nativeProfilerStartTool); registry.registerTool(nativeProfilerStopTool); registry.registerTool(nativeProfilerAnalyzeTool); + registry.registerTool(deviceLogsStartTool); + registry.registerTool(deviceLogsStopTool); registry.registerTool(profilerCpuQueryTool); registry.registerTool(profilerCommitQueryTool); registry.registerTool(profilerStackQueryTool); diff --git a/packages/tool-server/test/native-devtools-remote-sim-dep-gate.test.ts b/packages/tool-server/test/native-devtools-remote-sim-dep-gate.test.ts new file mode 100644 index 000000000..c240b683f --- /dev/null +++ b/packages/tool-server/test/native-devtools-remote-sim-dep-gate.test.ts @@ -0,0 +1,154 @@ +/** + * The native-devtools tools (native-describe-screen, native-find-views, …) + * support both local iOS simulators (driven via `xcrun simctl spawn`) and + * REMOTE iOS simulators (routed through `sim-remote`, no local xcrun needed) — + * their capability declares `appleRemote: { simulator: true }`. + * + * A regression once put a *global* `requires: ["xcrun"]` on these tools. The + * HTTP dispatcher probes `ToolDefinition.requires` unconditionally (before + * execution, regardless of the resolved device), so a remote sim on an + * xcrun-less host got a bogus 424 Failed Dependency even though it never needs + * xcrun. The correct gating is per device kind, done inside `execute` + * (`sim-remote` for remote sims, `xcrun` for local sims) — mirroring how + * `describe` declares its deps per branch. + * + * This pins that contract: remote sims are NOT xcrun-gated, local sims still + * are, and remote sims are gated on the RIGHT binary (sim-remote). + */ +import { describe, it, expect, beforeEach, vi } from "vitest"; +import request from "supertest"; +import { Registry, TypedEventEmitter } from "@argent/registry"; + +const execFileMock = vi.fn(); +vi.mock("node:child_process", async () => { + const actual = await vi.importActual("node:child_process"); + return { ...actual, execFile: (...args: unknown[]) => execFileMock(...args) }; +}); + +const resolveAndroidBinaryMock = vi.fn(); +vi.mock("../src/utils/android-binary", () => ({ + resolveAndroidBinary: (name: "adb" | "emulator") => resolveAndroidBinaryMock(name), + __resetAndroidBinaryCacheForTesting: () => {}, +})); + +import { createHttpApp } from "../src/http"; +import { __resetDepCacheForTests } from "../src/utils/check-deps"; +import { nativeDescribeScreenTool } from "../src/tools/native-devtools/native-describe-screen"; +import { nativeDevtoolsBlueprint, type NativeDevtoolsApi } from "../src/blueprints/native-devtools"; + +// Controls which host binaries the real dep-check treats as available: any dep +// in `missing` fails its `command -v` probe, everything else resolves. +function stubProbe(missing: readonly string[]): void { + execFileMock.mockImplementation( + ( + _cmd: string, + args: string[], + _opts: unknown, + cb: (err: Error | null, stdout?: string, stderr?: string) => void + ) => { + const script = args[1] ?? ""; + const dep = script.replace("command -v ", "").trim(); + if (missing.includes(dep)) cb(new Error(`not found: ${dep}`)); + else cb(null, `/usr/bin/${dep}\n`, ""); + } + ); + resolveAndroidBinaryMock.mockImplementation(async (name: string) => + missing.includes(name) ? null : `/usr/bin/${name}` + ); +} + +// Minimal fake NativeDevtools api so `execute` completes without any real +// simctl / sim-remote / socket I/O. `requiresAppRestart: true` makes the +// precheck return `restart_required`, a clean terminal result — the point is +// only that the request reaches execution instead of being 424'd at the gate. +function makeStubApi(): NativeDevtoolsApi { + return { + isEnvSetup: () => true, + socketPath: "/tmp/stub.sock", + ensureEnvReady: async () => {}, + reverifyEnv: async () => {}, + getInitFailure: () => null, + isConnected: () => false, + isAppRunning: async () => false, + listConnectedBundleIds: () => [], + requiresAppRestart: async () => true, + activateNetworkInspection: () => {}, + getNetworkLog: () => [], + clearNetworkLog: () => {}, + getAppState: async () => { + throw new Error("unused in this test"); + }, + detectFrontmostBundleId: async () => null, + queryViewHierarchy: async () => ({}), + }; +} + +function makeRegistry(): Registry { + const registry = new Registry(); + // Reuse the real blueprint's namespace/getURN but swap in a no-I/O factory so + // service resolution can't hang or shell out — the dep gating we're testing + // is entirely separate from the service factory. + registry.registerBlueprint({ + ...nativeDevtoolsBlueprint, + factory: async () => ({ + api: makeStubApi(), + dispose: async () => {}, + events: new TypedEventEmitter(), + }), + }); + registry.registerTool(nativeDescribeScreenTool); + return registry; +} + +const REMOTE_UDID = "remote:12345678-1234-1234-1234-123456789012"; +const LOCAL_UDID = "12345678-1234-1234-1234-123456789012"; +// Physical iPhone UDID shape (8-hex ECID, single dash, 16 hex). +const PHYSICAL_UDID = "00008120-000E6D0C0ABBA01E"; + +describe("native-devtools tools — per-device-kind dependency gating", () => { + beforeEach(() => { + __resetDepCacheForTests(); + execFileMock.mockReset(); + resolveAndroidBinaryMock.mockReset(); + }); + + it("does NOT block a REMOTE sim on missing xcrun (routes via sim-remote)", async () => { + stubProbe(["xcrun"]); // xcrun absent, sim-remote present + const { app } = createHttpApp(makeRegistry()); + const res = await request(app) + .post("/tools/native-describe-screen") + .send({ udid: REMOTE_UDID, bundleId: "com.example.app" }); + // Reaches execution instead of the xcrun preflight 424. + expect(res.status).not.toBe(424); + expect(res.status).toBe(200); + }); + + it("still gates a LOCAL sim on xcrun (via the in-execute dep check)", async () => { + stubProbe(["xcrun"]); + const { app } = createHttpApp(makeRegistry()); + const res = await request(app) + .post("/tools/native-describe-screen") + .send({ udid: LOCAL_UDID, bundleId: "com.example.app" }); + expect(res.status).toBe(424); + expect(res.body.missing).toEqual(["xcrun"]); + }); + + it("gates a REMOTE sim on the RIGHT binary — sim-remote, not xcrun", async () => { + stubProbe(["xcrun", "sim-remote"]); // both absent + const { app } = createHttpApp(makeRegistry()); + const res = await request(app) + .post("/tools/native-describe-screen") + .send({ udid: REMOTE_UDID, bundleId: "com.example.app" }); + expect(res.status).toBe(424); + expect(res.body.missing).toEqual(["sim-remote"]); + }); + + it("rejects a PHYSICAL iOS device at the capability gate (path unaffected)", async () => { + stubProbe([]); // all deps present — must still be rejected before deps + const { app } = createHttpApp(makeRegistry()); + const res = await request(app) + .post("/tools/native-describe-screen") + .send({ udid: PHYSICAL_UDID, bundleId: "com.example.app" }); + expect(res.status).toBe(400); + }); +}); diff --git a/packages/tool-server/test/physical-ios-coredevice-sidecar.test.ts b/packages/tool-server/test/physical-ios-coredevice-sidecar.test.ts new file mode 100644 index 000000000..38b12f74f --- /dev/null +++ b/packages/tool-server/test/physical-ios-coredevice-sidecar.test.ts @@ -0,0 +1,164 @@ +/** + * Coverage for the physical-iOS (CoreDevice) backend after it moved from a + * per-call `pymobiledevice3` CLI spawn to a persistent stdio sidecar: + * + * - `adaptCoreDeviceAxToDescribeResult` — the axAudit accessibility tree → + * describe adapter that backs `describe` on a real iPhone. Pins caption→role + * parsing, label cleanup, rect normalization, and frame interpolation for the + * elements the audit didn't rect (every frame stays in [0,1]). + * - `agentError` — the 9021 (iOS-27 host-input gate) message mapping. + * - `CoreDeviceAgent` — the stdio JSON protocol: ready handshake, id-correlated + * request/response, and error propagation (via a stand-in node process). + */ +import { describe, it, expect } from "vitest"; +import { writeFileSync } from "node:fs"; +import { join } from "node:path"; +import { tmpdir } from "node:os"; +import { FAILURE_CODES, getFailureSignal } from "@argent/registry"; +import { adaptCoreDeviceAxToDescribeResult } from "../src/tools/describe/platforms/ios/ios-coredevice-ax-adapter"; +import { agentError } from "../src/blueprints/core-device"; +import { CoreDeviceAgent, CoreDeviceAgentError } from "../src/blueprints/coredevice-agent"; + +interface Node { + role: string; + frame: { x: number; y: number; width: number; height: number }; + children: Node[]; + label?: string; +} +function flatten(n: Node, out: Node[] = []): Node[] { + out.push(n); + for (const c of n.children) flatten(c, out); + return out; +} +const center = (f: Node["frame"]) => ({ x: f.x + f.width / 2, y: f.y + f.height / 2 }); + +// A realistic axAudit snapshot: some elements carry an audit rect (points on a +// 393x852 screen), others don't (interpolated by the adapter). +const AXTREE = { + screen: { w: 393, h: 852 }, + elements: [ + { caption: "Settings, Button", id: "a1", rect: "{{318, 63}, {55, 36}}" }, + { caption: "Wi-Fi, Header", id: "a2", rect: "{{32, 168}, {55, 26}}" }, + { caption: "Wi-Fi, 1, Button, Toggle", id: "a3" }, // no rect -> interpolated + { caption: "Other…, Button", id: "a4", rect: "{{16, 553}, {361, 52}}" }, + { caption: "Known networks will be joined automatically.", id: "a5" }, // static text + ], +}; + +describe("adaptCoreDeviceAxToDescribeResult", () => { + const tree = adaptCoreDeviceAxToDescribeResult(AXTREE); + const nodes = flatten(tree as Node); + const byLabel = (l: string) => nodes.find((n) => n.label === l); + + it("parses roles from caption traits and strips them from the label", () => { + expect(byLabel("Settings")?.role).toBe("AXButton"); + expect(byLabel("Wi-Fi")?.role).toBe("AXHeader"); + // Button trait wins the role; trailing Button/Toggle stripped from the label. + expect(byLabel("Wi-Fi, 1")?.role).toBe("AXButton"); + // No trait -> static text, full caption kept as label. + const stat = nodes.find((n) => n.label?.startsWith("Known networks")); + expect(stat?.role).toBe("AXStaticText"); + }); + + it("normalizes an audited rect (points) into a [0,1] frame", () => { + const other = byLabel("Other…")!; + // {{16, 553}, {361, 52}} on 393x852 + expect(other.frame.x).toBeCloseTo(16 / 393, 3); + expect(other.frame.y).toBeCloseTo(553 / 852, 3); + expect(other.frame.width).toBeCloseTo(361 / 393, 3); + }); + + it("interpolates a rect-less element between its neighbours (reading order)", () => { + const wifiHeader = center(byLabel("Wi-Fi")!.frame).y; // ~168/852 + const other = center(byLabel("Other…")!.frame).y; // ~553/852 + const toggle = center(byLabel("Wi-Fi, 1")!.frame).y; // no rect, between the two + expect(toggle).toBeGreaterThan(wifiHeader); + expect(toggle).toBeLessThan(other); + }); + + it("keeps every frame within the normalized [0,1] box", () => { + for (const n of nodes) { + const { x, y, width, height } = n.frame; + for (const v of [x, y, width, height]) { + expect(v).toBeGreaterThanOrEqual(0); + expect(v).toBeLessThanOrEqual(1); + } + expect(x + width).toBeLessThanOrEqual(1.0001); + expect(y + height).toBeLessThanOrEqual(1.0001); + } + }); + + it("does not throw on an empty / screen-less tree", () => { + expect(() => adaptCoreDeviceAxToDescribeResult({ elements: [] })).not.toThrow(); + expect(() => + adaptCoreDeviceAxToDescribeResult({ + elements: [{ caption: "x", id: "1" }], + }) + ).not.toThrow(); + }); +}); + +describe("agentError — iOS-27 host-input gate (CoreDeviceError 9021)", () => { + it("maps a gated agent error to the actionable iOS-27 message", () => { + const e = agentError("tap", new CoreDeviceAgentError("… CoreDeviceError 9021 …", true)); + expect(e.message).toContain("requires iOS 27+"); + expect(getFailureSignal(e)?.error_code).toBe(FAILURE_CODES.CORE_DEVICE_IOS_VERSION_TOO_OLD); + }); + + it("maps a non-gated agent error to a generic command failure", () => { + const e = agentError("swipe", new CoreDeviceAgentError("some other failure", false)); + expect(e.message).toContain("CoreDevice swipe failed"); + expect(e.message).not.toContain("iOS 27"); + expect(getFailureSignal(e)?.error_code).toBe(FAILURE_CODES.CORE_DEVICE_COMMAND_FAILED); + }); + + it("maps a plain Error to a command failure", () => { + const e = agentError("button", new Error("boom")); + expect(getFailureSignal(e)?.error_code).toBe(FAILURE_CODES.CORE_DEVICE_COMMAND_FAILED); + }); +}); + +describe("CoreDeviceAgent — stdio JSON protocol", () => { + // A stand-in for the python agent: emits the ready handshake, echoes ops, and + // returns a gated error for op "fail". Run with node so the test needs no + // device or pymobiledevice3. + const mock = join(tmpdir(), `argent-mock-coredevice-agent-${process.pid}.cjs`); + writeFileSync( + mock, + `const rl = require("readline").createInterface({ input: process.stdin }); +process.stdout.write(JSON.stringify({ ready: true }) + "\\n"); +rl.on("line", (l) => { + const m = JSON.parse(l); + if (m.op === "fail") { + process.stdout.write(JSON.stringify({ id: m.id, error: "CoreDeviceError 9021", gated_9021: true }) + "\\n"); + } else { + process.stdout.write(JSON.stringify({ id: m.id, ok: true, echo: m.op }) + "\\n"); + } +}); +` + ); + + it("handshakes, correlates responses by id, and propagates errors", async () => { + const agent = new CoreDeviceAgent(process.execPath, mock, "UDID", 49151, 5000); + await agent.start(); + try { + const r = await agent.request("ping"); + expect(r.ok).toBe(true); + expect(r.echo).toBe("ping"); + + await expect(agent.request("fail")).rejects.toMatchObject({ + name: "CoreDeviceAgentError", + gated9021: true, + }); + } finally { + agent.dispose(); + } + }); + + it("rejects a request made after dispose", async () => { + const agent = new CoreDeviceAgent(process.execPath, mock, "UDID", 49151, 5000); + await agent.start(); + agent.dispose(); + await expect(agent.request("ping")).rejects.toThrow(); + }); +}); diff --git a/packages/tool-server/test/physical-ios-followups.test.ts b/packages/tool-server/test/physical-ios-followups.test.ts new file mode 100644 index 000000000..1dc90b19a --- /dev/null +++ b/packages/tool-server/test/physical-ios-followups.test.ts @@ -0,0 +1,359 @@ +/** + * Follow-up coverage for physical-iOS automation. These tests pin + * behaviors that the original physical-ios.test.ts left uncovered and that a + * regression could silently break: + * + * - discovery must NOT surface the host's iOS simulators as phantom physical + * devices (devicectl enumerates them with transportType "sameMachine"); + * - the `button` WDA mapping + rejection of unsupported App Switcher; + * - the privileged-tunnel flag gate (root escalation must be opt-in); + * - parity tools advertise physical-device support while simulator-only + * native injection tools remain gated; + * - run-sequence must not eagerly hold simulator-server for a physical iPhone; + * - swipe duration clamping + timeout scaling. + */ +import { describe, it, expect, vi } from "vitest"; + +// core-device is the only module under test that reads the feature flag; mock it +// so the flag gate can be exercised deterministically regardless of the host's +// ~/.argent/flags.json. (See variant-flag-gate.test.ts for the same pattern.) +vi.mock("@argent/configuration-core", () => ({ isFlagEnabled: vi.fn() })); +import { isFlagEnabled } from "@argent/configuration-core"; + +import { resolveDevice, isPhysicalIosUdid } from "../src/utils/device-info"; +import { parsePhysicalIosDevices } from "../src/utils/ios-devices"; +import { UnsupportedOperationError, assertSupported } from "../src/utils/capability"; +import { + swipeDragParams, + ensureCoreDeviceTunnel, + assertPhysicalIosEnabled, +} from "../src/blueprints/core-device"; +import { buttonTool } from "../src/tools/button"; +import { createRunSequenceTool } from "../src/tools/run-sequence"; +import { describeIos } from "../src/tools/describe/platforms/ios"; +import { makeIosImpl as makeLaunchAppIosImpl } from "../src/tools/launch-app/platforms/ios"; +import { iosImpl as openUrlIosImpl } from "../src/tools/open-url/platforms/ios"; +import { makeIosImpl as makeRestartAppIosImpl } from "../src/tools/restart-app/platforms/ios"; +import { gestureSwipeTool } from "../src/tools/gesture-swipe"; +import { gestureTapTool } from "../src/tools/gesture-tap"; +import { createKeyboardTool } from "../src/tools/keyboard"; +import { gesturePinchTool } from "../src/tools/gesture-pinch"; +import { screenshotDiffTool } from "../src/tools/screenshot-diff"; +import { nativeDescribeScreenTool } from "../src/tools/native-devtools/native-describe-screen"; +import { nativeProfilerStartTool } from "../src/tools/profiler/native-profiler/native-profiler-start"; + +const mockFlag = vi.mocked(isFlagEnabled); + +// Physical-iOS branches of both handlers throw/reject before ever touching +// `registry` (see the assertions below), so a stub registry is safe here. +const launchAppIos = makeLaunchAppIosImpl({} as never); +const keyboardTool = createKeyboardTool({} as never); + +const PHYSICAL_UDID = "00008120-000E6D0C0ABBA01E"; +const SIM_UDID = "AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA"; + +describe("discovery does not surface simulators as physical devices", () => { + // Real `xcrun devicectl list devices` JSON also lists every host iOS + // simulator (platform "iOS", transportType "sameMachine"); without the + // UDID-shape gate those leak in as phantom physical devices. + const data = { + result: { + devices: [ + // The real, connected iPhone — KEPT. + { + hardwareProperties: { udid: PHYSICAL_UDID, platform: "iOS", productType: "iPhone15,4" }, + deviceProperties: { name: "Real iPhone" }, + connectionProperties: { transportType: "wired", tunnelState: "disconnected" }, + }, + // A booted iOS simulator (UUID shape, sameMachine transport) — DROPPED. + { + hardwareProperties: { udid: SIM_UDID, platform: "iOS", productType: "iPhone17,2" }, + deviceProperties: { name: "iPhone 16 Pro Max" }, + connectionProperties: { transportType: "sameMachine", tunnelState: "connected" }, + }, + // A shut-down simulator (UUID shape) — DROPPED (would otherwise be + // reported as a "connected" device). + { + hardwareProperties: { + udid: "39646432-58B6-4A21-923A-00F0EDE4FF81", + platform: "iOS", + productType: "iPhone17,3", + }, + deviceProperties: { name: "iPhone 16" }, + connectionProperties: { transportType: "sameMachine", tunnelState: "disconnected" }, + }, + // A paired-but-offline real device (physical shape, no transport) — DROPPED. + { + hardwareProperties: { + udid: "00008030-00096526219B802E", + platform: "iOS", + productType: "iPhone12,8", + }, + deviceProperties: { name: "Old iPhone" }, + connectionProperties: { tunnelState: "unavailable" }, + }, + ], + }, + }; + + it("returns only the real physical iPhone", () => { + const out = parsePhysicalIosDevices(data); + expect(out).toEqual([ + { udid: PHYSICAL_UDID, name: "Real iPhone", productType: "iPhone15,4", state: "connected" }, + ]); + }); + + it("every returned device has a physical-shape UDID", () => { + for (const d of parsePhysicalIosDevices(data)) { + expect(isPhysicalIosUdid(d.udid)).toBe(true); + } + }); +}); + +describe("button — WDA mapping on physical iOS", () => { + const press = async (button: string) => { + const physicalIos = { button: vi.fn().mockResolvedValue(undefined) }; + const res = await buttonTool.execute( + { physicalIos } as never, + { + udid: PHYSICAL_UDID, + button, + } as never + ); + return { physicalIos, res }; + }; + + it("maps all supported buttons to the physical automation API", async () => { + for (const [argent, backend] of [ + ["home", "home"], + ["power", "power"], + ["volumeUp", "volumeUp"], + ["volumeDown", "volumeDown"], + ["actionButton", "actionButton"], + ]) { + const { physicalIos, res } = await press(argent); + expect(physicalIos.button).toHaveBeenCalledWith(backend); + expect(res).toEqual({ pressed: argent }); + } + }); + + it("rejects App Switcher, which XCTest does not expose", async () => { + await expect(press("appSwitch")).rejects.toBeInstanceOf(UnsupportedOperationError); + }); + + it("does not resolve WDA for unsupported App Switcher", () => { + const services = buttonTool.services!({ udid: PHYSICAL_UDID, button: "appSwitch" } as never); + expect(services.physicalIos).toBeUndefined(); + }); + + it("resolves the physical automation service for a supported button", () => { + const services = buttonTool.services!({ udid: PHYSICAL_UDID, button: "home" } as never); + expect(services.physicalIos).toBeDefined(); + }); +}); + +describe("privileged tunnel start is gated behind the feature flag", () => { + it("rejects with the enable hint when physical-ios-devices is off", async () => { + mockFlag.mockReturnValue(false); + await expect(ensureCoreDeviceTunnel(PHYSICAL_UDID)).rejects.toThrow( + /Physical iOS support is disabled.*argent enable physical-ios-devices/s + ); + expect(mockFlag).toHaveBeenCalledWith("physical-ios-devices"); + }); +}); + +describe("launch-app enforces the physical-iOS flag (no bypass)", () => { + // launch-app drives a real device via `devicectl` directly (not the + // CoreDevice service), so unlike screenshot/tap/swipe it must enforce the + // opt-in itself — otherwise it would be the one physical-iOS operation + // reachable while the feature is disabled. + it("assertPhysicalIosEnabled throws when the flag is off, not when on", () => { + mockFlag.mockReturnValue(false); + expect(() => assertPhysicalIosEnabled()).toThrow(/Physical iOS support is disabled/); + mockFlag.mockReturnValue(true); + expect(() => assertPhysicalIosEnabled()).not.toThrow(); + }); + + it("launch-app rejects a physical iPhone when the flag is off (before shelling devicectl)", async () => { + mockFlag.mockReturnValue(false); + await expect( + launchAppIos.handler( + {} as never, + { udid: PHYSICAL_UDID, bundleId: "com.apple.Preferences" } as never, + resolveDevice(PHYSICAL_UDID) + ) + ).rejects.toThrow(/Physical iOS support is disabled.*argent enable physical-ios-devices/s); + }); +}); + +describe("physical iOS app lifecycle parity", () => { + const device = resolveDevice(PHYSICAL_UDID); + + it("opens deep links through the persistent physical automation session", async () => { + const openUrl = vi.fn().mockResolvedValue(undefined); + const result = await openUrlIosImpl.handler( + { physicalIos: { openUrl } } as never, + { udid: PHYSICAL_UDID, url: "maps://?q=Bangalore+Palace" }, + device + ); + + expect(openUrl).toHaveBeenCalledWith("maps://?q=Bangalore+Palace"); + expect(result).toEqual({ opened: true, url: "maps://?q=Bangalore+Palace" }); + }); + + it("terminates and relaunches apps through the persistent physical automation session", async () => { + const terminateApp = vi.fn().mockResolvedValue(true); + const launchApp = vi.fn().mockResolvedValue(undefined); + const registry = { + resolveService: vi.fn().mockResolvedValue({ terminateApp, launchApp }), + }; + const restart = makeRestartAppIosImpl(registry as never); + const result = await restart.handler( + {}, + { udid: PHYSICAL_UDID, bundleId: "com.apple.Maps" }, + device + ); + + expect(terminateApp).toHaveBeenCalledWith("com.apple.Maps"); + expect(launchApp).toHaveBeenCalledWith("com.apple.Maps"); + expect(result).toEqual({ restarted: true, bundleId: "com.apple.Maps" }); + }); +}); + +describe("gesture-swipe routes physical iOS to WDA", () => { + it("forwards normalized coords and duration to physicalIos.swipe", async () => { + const physicalIos = { swipe: vi.fn().mockResolvedValue(undefined) }; + const res = await gestureSwipeTool.execute( + { physicalIos } as never, + { + udid: PHYSICAL_UDID, + fromX: 0.5, + fromY: 0.7, + toX: 0.5, + toY: 0.3, + durationMs: 250, + } as never + ); + expect(physicalIos.swipe).toHaveBeenCalledWith(0.5, 0.7, 0.5, 0.3, 250); + expect(res).toMatchObject({ swiped: true }); + }); + + it("defaults the duration to 300ms when omitted", async () => { + const physicalIos = { swipe: vi.fn().mockResolvedValue(undefined) }; + await gestureSwipeTool.execute( + { physicalIos } as never, + { + udid: PHYSICAL_UDID, + fromX: 0.2, + fromY: 0.2, + toX: 0.8, + toY: 0.8, + } as never + ); + expect(physicalIos.swipe).toHaveBeenCalledWith(0.2, 0.2, 0.8, 0.8, 300); + }); +}); + +describe("physical iOS describe", () => { + const device = resolveDevice(PHYSICAL_UDID); + + it("returns the live WDA accessibility tree", async () => { + const registry = { + resolveService: async () => ({ + source: async () => + '', + windowSize: async () => ({ width: 393, height: 852 }), + }), + }; + const result = await describeIos(registry as never, device, {}); + expect(result.source).toBe("wda-ax"); + const flat = JSON.stringify(result.tree); + expect(flat).toContain("General"); + expect(result.hint).toContain("WebDriverAgent"); + }); +}); + +describe("run-sequence does not eagerly hold simulator-server for physical iOS", () => { + // run-sequence never eagerly declares any service (each step resolves its own + // via invokeSubTool) — a physical iOS udid must not eagerly hold + // simulator-server, which would throw on a `kind === "device"` target before + // step 1 even runs. Simulators go through the same lazy path. + const tool = createRunSequenceTool({} as never); + const params = (udid: string) => ({ udid, steps: [{ tool: "gesture-tap", args: {} }] }); + + it("holds no simulator-server service for a physical iPhone", () => { + const services = tool.services(params(PHYSICAL_UDID)); + expect(services.simulatorServer).toBeUndefined(); + expect(Object.keys(services)).toHaveLength(0); + }); + + it("holds no simulator-server service for a simulator either", () => { + const services = tool.services(params(SIM_UDID)); + expect(services.simulatorServer).toBeUndefined(); + expect(Object.keys(services)).toHaveLength(0); + }); +}); + +describe("capability matrix is honest about physical-iOS support (clean 400 at the gate)", () => { + const physical = resolveDevice(PHYSICAL_UDID); + const sim = resolveDevice(SIM_UDID); + const androidEmu = resolveDevice("emulator-5554"); + + it("supported tools accept a physical iPhone", () => { + expect(() => assertSupported("gesture-tap", gestureTapTool.capability, physical)).not.toThrow(); + expect(() => assertSupported("button", buttonTool.capability, physical)).not.toThrow(); + expect(() => assertSupported("keyboard", keyboardTool.capability, physical)).not.toThrow(); + expect(() => assertSupported("gesture-pinch", gesturePinchTool.capability, physical)).not.toThrow(); + expect(() => + assertSupported("screenshot-diff", screenshotDiffTool.capability, physical) + ).not.toThrow(); + expect(() => + assertSupported("native-profiler-start", nativeProfilerStartTool.capability, physical) + ).not.toThrow(); + }); + + it("simulator-only tools reject a physical iPhone via the capability gate", () => { + for (const [id, cap] of [ + ["native-describe-screen", nativeDescribeScreenTool.capability], + ] as const) { + expect(() => assertSupported(id, cap, physical)).toThrow(UnsupportedOperationError); + // ...but still work on a simulator (no regression to simulator support). + expect(() => assertSupported(id, cap, sim)).not.toThrow(); + } + }); + + it("native-profiler-start still accepts a physical Android device", () => { + expect(() => + assertSupported("native-profiler-start", nativeProfilerStartTool.capability, androidEmu) + ).not.toThrow(); + }); +}); + +describe("swipeDragParams — clamping and timeout scaling", () => { + it("clamps a typical swipe and scales the timeout past the drag duration", () => { + const p = swipeDragParams(300); + expect(p.seconds).toBe("0.300"); + expect(p.steps).toBe(19); + expect(p.timeoutMs).toBe(15_300); + }); + + it("a long swipe gets a timeout that outlasts the drag (the bug this fixes)", () => { + const p = swipeDragParams(20_000); + expect(p.seconds).toBe("20.000"); + expect(p.steps).toBe(60); // step count is capped + expect(p.timeoutMs).toBe(35_000); + expect(p.timeoutMs).toBeGreaterThan(20_000); + }); + + it("floors degenerate (zero/negative) durations to a real dwell", () => { + expect(swipeDragParams(0).seconds).toBe("0.050"); + expect(swipeDragParams(-100).seconds).toBe("0.050"); + expect(swipeDragParams(0).steps).toBeGreaterThanOrEqual(2); + }); + + it("caps a pathological duration", () => { + const p = swipeDragParams(10_000_000); + expect(p.seconds).toBe("60.000"); + expect(p.timeoutMs).toBe(75_000); + }); +}); diff --git a/packages/tool-server/test/physical-ios-wda-parity.test.ts b/packages/tool-server/test/physical-ios-wda-parity.test.ts new file mode 100644 index 000000000..19b4334b3 --- /dev/null +++ b/packages/tool-server/test/physical-ios-wda-parity.test.ts @@ -0,0 +1,89 @@ +import { describe, expect, it } from "vitest"; +import { compactPhysicalTouchEvents } from "../src/blueprints/physical-ios-automation"; +import { adaptWdaSourceToDescribeResult } from "../src/tools/describe/platforms/ios/ios-wda-ax-adapter"; +import { formatDescribeTree } from "../src/tools/describe/format-tree"; +import { physicalAllProcessesStrategy } from "../src/utils/ios-profiler/capture-strategy/physical-all-processes"; + +describe("physical iOS touch compaction", () => { + it("collapses interpolated straight-line points and preserves total duration", () => { + const compacted = compactPhysicalTouchEvents([ + { type: "Down", x: 0.2, y: 0.5, delayMs: 0 }, + { type: "Move", x: 0.4, y: 0.5, delayMs: 100 }, + { type: "Move", x: 0.6, y: 0.5, delayMs: 100 }, + { type: "Up", x: 0.8, y: 0.5, delayMs: 100 }, + ]); + + expect(compacted).toHaveLength(2); + expect(compacted[1]).toMatchObject({ type: "Up", x: 0.8, delayMs: 300 }); + }); + + it("retains vertices needed to represent a curved rotation path", () => { + const compacted = compactPhysicalTouchEvents([ + { type: "Down", x: 0.7, y: 0.5, delayMs: 0 }, + { type: "Move", x: 0.64, y: 0.64, delayMs: 100 }, + { type: "Move", x: 0.5, y: 0.7, delayMs: 100 }, + { type: "Up", x: 0.36, y: 0.64, delayMs: 100 }, + ]); + + expect(compacted.length).toBeGreaterThan(2); + expect(compacted.reduce((sum, event) => sum + (event.delayMs ?? 0), 0)).toBe(300); + }); + + it("does not compact across multiple contact sequences", () => { + const events = [ + { type: "Down" as const, x: 0.2, y: 0.2 }, + { type: "Up" as const, x: 0.2, y: 0.2 }, + { type: "Down" as const, x: 0.8, y: 0.8 }, + { type: "Up" as const, x: 0.8, y: 0.8 }, + ]; + expect(compactPhysicalTouchEvents(events)).toEqual(events); + }); +}); + +describe("physical iOS WDA describe adapter", () => { + it("keeps nested descendants and renders their normalized frames", () => { + const xml = ` + + + + + + + + + + `; + const tree = adaptWdaSourceToDescribeResult(xml, { width: 400, height: 800 }); + const rendered = formatDescribeTree(tree, { source: "wda-ax" }); + + expect(rendered).toContain("Mode: nested"); + expect(rendered).toContain('AXSearchField "Apple Maps" value="Bangalore"'); + expect(rendered).toContain("(0.100, 0.125, 0.800, 0.060)"); + expect(rendered).toContain('AXButton "Directions"'); + }); +}); + +describe("physical iOS profiling strategy", () => { + it("records all physical-device processes and filters analysis to the app pid", () => { + const target = { executable: "Maps", pid: 1835 }; + const args = physicalAllProcessesStrategy.buildRecordArgs({ + templatePath: "Time Profiler", + deviceId: "00008130-0018544622E1001C", + target, + outputFile: "/tmp/maps.trace", + }); + + expect(args).toEqual([ + "record", + "--template", + "Time Profiler", + "--device", + "00008130-0018544622E1001C", + "--all-processes", + "--output", + "/tmp/maps.trace", + "--no-prompt", + ]); + expect(physicalAllProcessesStrategy.cpuFilterPid(target)).toBe(1835); + }); +}); diff --git a/packages/tool-server/test/physical-ios.test.ts b/packages/tool-server/test/physical-ios.test.ts new file mode 100644 index 000000000..39fbcbff9 --- /dev/null +++ b/packages/tool-server/test/physical-ios.test.ts @@ -0,0 +1,215 @@ +import { describe, it, expect } from "vitest"; +import { + classifyDevice, + resolveDevice, + isPhysicalIos, + isPhysicalIosUdid, +} from "../src/utils/device-info"; +import { parsePhysicalIosDevices } from "../src/utils/ios-devices"; +import { toHid, tunneldStartCommand, appleScriptQuote } from "../src/blueprints/core-device"; +import { createLaunchAppTool } from "../src/tools/launch-app"; +import { createRestartAppTool } from "../src/tools/restart-app"; +import { devicesToPreviewEntries } from "../src/preview"; +import type { ListDevicesResult } from "../src/tools/devices/list-devices"; + +// A real iPhone's UDID: 8-hex ECID, one dash, 16 hex. +const PHYSICAL_UDID = "00008120-000E6D0C0ABBA01E"; +const SIM_UDID = "AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA"; + +describe("physical iOS classification", () => { + it("classifies a physical iPhone UDID as ios", () => { + expect(classifyDevice(PHYSICAL_UDID)).toBe("ios"); + }); + + it("resolves a physical iPhone UDID to ios + device", () => { + const d = resolveDevice(PHYSICAL_UDID); + expect(d.platform).toBe("ios"); + expect(d.kind).toBe("device"); + expect(d.id).toBe(PHYSICAL_UDID); + }); + + it("still resolves a simulator UUID to ios + simulator", () => { + const d = resolveDevice(SIM_UDID); + expect(d.platform).toBe("ios"); + expect(d.kind).toBe("simulator"); + }); + + it("isPhysicalIosUdid distinguishes device from simulator shapes", () => { + expect(isPhysicalIosUdid(PHYSICAL_UDID)).toBe(true); + expect(isPhysicalIosUdid(SIM_UDID)).toBe(false); + }); + + it("isPhysicalIos is true only for ios+device", () => { + expect(isPhysicalIos(resolveDevice(PHYSICAL_UDID))).toBe(true); + expect(isPhysicalIos(resolveDevice(SIM_UDID))).toBe(false); + expect(isPhysicalIos(resolveDevice("emulator-5554"))).toBe(false); + }); + + it("does not reclassify Android serials as physical iOS", () => { + // No 8hex-16hex Android serials in the wild; guard against regressions. + expect(classifyDevice("HT82A0203045")).toBe("android"); + expect(classifyDevice("192.168.1.5:5555")).toBe("android"); + expect(resolveDevice("HT82A0203045").kind).toBe("device"); + }); +}); + +describe("parsePhysicalIosDevices (devicectl JSON)", () => { + const sample = { + result: { + devices: [ + // Connected iPhone — kept. + { + hardwareProperties: { udid: PHYSICAL_UDID, platform: "iOS", productType: "iPhone15,4" }, + deviceProperties: { name: "iPhone 15" }, + connectionProperties: { transportType: "wired", tunnelState: "disconnected" }, + }, + // Paired but offline (no transportType, tunnelState unavailable) — dropped. + { + hardwareProperties: { + udid: "00008030-00096526219B802E", + platform: "iOS", + productType: "iPhone12,8", + }, + deviceProperties: { name: "Old iPhone" }, + connectionProperties: { tunnelState: "unavailable" }, + }, + // A connected Apple Watch — dropped (not iOS). + { + hardwareProperties: { + udid: "11111111-2222222222222222", + platform: "watchOS", + productType: "Watch7,1", + }, + deviceProperties: { name: "Watch" }, + connectionProperties: { transportType: "wired" }, + }, + ], + }, + }; + + it("keeps only connected iOS devices with the right fields", () => { + const out = parsePhysicalIosDevices(sample); + expect(out).toEqual([ + { udid: PHYSICAL_UDID, name: "iPhone 15", productType: "iPhone15,4", state: "connected" }, + ]); + }); + + it("returns [] for empty/missing input", () => { + expect(parsePhysicalIosDevices({})).toEqual([]); + expect(parsePhysicalIosDevices({ result: { devices: [] } })).toEqual([]); + }); +}); + +describe("toHid (normalized 0..1 → 0..65535)", () => { + it("maps endpoints and midpoint", () => { + expect(toHid(0)).toBe(0); + expect(toHid(1)).toBe(65535); + expect(toHid(0.5)).toBe(32768); + }); + + it("clamps out-of-range input", () => { + expect(toHid(-0.2)).toBe(0); + expect(toHid(1.5)).toBe(65535); + }); +}); + +describe("privileged tunnel start command", () => { + it("single-quotes the binary path, pins HOME, passes the port + daemonize flag", () => { + const cmd = tunneldStartCommand("/Users/me/.local/bin/pymobiledevice3", 49151); + expect(cmd).toContain("HOME=/var/root"); + expect(cmd).toContain("'/Users/me/.local/bin/pymobiledevice3' remote tunneld --port 49151 -d"); + }); + + it("escapes a single quote in the path so it can't break out of the sh word", () => { + const cmd = tunneldStartCommand("/Users/o'brien/pmd3", 5000); + // ' -> '\'' so the whole path stays one shell word + expect(cmd).toContain(`'/Users/o'\\''brien/pmd3' remote tunneld`); + }); + + it("escapes backslashes and double-quotes for the AppleScript string literal", () => { + expect(appleScriptQuote('a "b" c')).toBe('a \\"b\\" c'); + expect(appleScriptQuote("a\\b")).toBe("a\\\\b"); + }); +}); + +describe("lifecycle tools don't eagerly resolve native-devtools for local iOS", () => { + // Regression guard: the registry resolves a tool's services() BEFORE execute(), + // and the native-devtools service throws a simulator-only guard for physical + // devices. So eagerly resolving it for a physical iPhone would break launch-app + // (a supported tool) and mask restart-app's intended rejection message. Local + // iOS (simulator and physical alike) resolves native-devtools lazily inside the + // handler instead — only ios-remote declares it eagerly via services(). + const launchAppTool = createLaunchAppTool({} as never); + const restartAppTool = createRestartAppTool({} as never); + const params = (udid: string) => ({ udid, bundleId: "com.apple.Preferences" }); + + it("launch-app: omitted for both physical device and simulator", () => { + expect(launchAppTool.services(params(PHYSICAL_UDID)).nativeDevtools).toBeUndefined(); + expect(launchAppTool.services(params(SIM_UDID)).nativeDevtools).toBeUndefined(); + }); + + it("restart-app: omitted for both physical device and simulator", () => { + expect(restartAppTool.services(params(PHYSICAL_UDID)).nativeDevtools).toBeUndefined(); + expect(restartAppTool.services(params(SIM_UDID)).nativeDevtools).toBeUndefined(); + }); +}); + +describe("preview target list excludes targets it can't stream", () => { + // Regression guard: the preview / Lens UI streams frames over simulator-server, + // which refuses physical iOS (kind === "device", driven over CoreDevice) and + // never serves Chromium. Those must not leak into the selectable target list. + const devices: ListDevicesResult["devices"] = [ + { + platform: "ios", + udid: SIM_UDID, + name: "iPhone 16 Pro", + state: "Booted", + kind: "simulator", + runtime: "com.apple.CoreSimulator.SimRuntime.iOS-18-5", + }, + { + platform: "ios", + udid: PHYSICAL_UDID, + name: "iPhone 15", + state: "connected", + kind: "device", + productType: "iPhone15,4", + }, + { + platform: "android", + serial: "emulator-5554", + state: "device", + isEmulator: true, + kind: "emulator", + model: "Pixel 7", + avdName: "Pixel_7_API_33", + sdkLevel: 34, + }, + { + platform: "chromium", + id: "chromium-1", + title: "App", + port: 9222, + url: "about:blank", + browser: "Chrome/120", + state: "Running", + }, + ]; + + it("includes the simulator and the Android emulator", () => { + const entries = devicesToPreviewEntries(devices); + expect(entries.map((e) => e.udid).sort()).toEqual([SIM_UDID, "emulator-5554"].sort()); + }); + + it("excludes the physical iPhone and the Chromium app", () => { + const entries = devicesToPreviewEntries(devices); + expect(entries.some((e) => e.udid === PHYSICAL_UDID)).toBe(false); + expect(entries.some((e) => e.platform !== "ios" && e.platform !== "android")).toBe(false); + }); + + it("keeps `runtime` a string for a simulator that reports one", () => { + const [sim] = devicesToPreviewEntries(devices); + expect(typeof sim!.runtime).toBe("string"); + expect(sim!.runtime.length).toBeGreaterThan(0); + }); +}); diff --git a/scripts/download-native-binaries.sh b/scripts/download-native-binaries.sh index 534f5afac..181166f6a 100755 --- a/scripts/download-native-binaries.sh +++ b/scripts/download-native-binaries.sh @@ -63,6 +63,23 @@ gh release download "${TAG}" \ --clobber chmod +x "${IOS_BIN_DIR}/ax-service" +# argent-device-auth is the macOS host helper that shows the branded admin +# prompt to start the physical-iOS CoreDevice tunnel as root. Like ax-service it +# is a darwin-only binary, resolved at bin/darwin/argent-device-auth. +# Optional: until the argent-private build publishes it, the release won't carry +# it — physical-iOS then falls back to the (unbranded) osascript admin prompt, +# so a missing asset must not fail the whole download. +echo " Downloading argent-device-auth..." +if gh release download "${TAG}" \ + --repo "${REPO}" \ + --pattern "argent-device-auth" \ + --dir "${IOS_BIN_DIR}" \ + --clobber 2>/dev/null; then + chmod +x "${IOS_BIN_DIR}/argent-device-auth" +else + echo " (not in this release yet — physical iOS will use the osascript prompt fallback)" +fi + # tvOS binaries (Apple TV support). The three tvOS injection dylibs share their # filenames with the iOS dylibs, so the release ships them as a tarball # (native-devtools-ios-tvos-dylibs.tar.gz) that we extract into dylibs/tvos/ — @@ -223,6 +240,7 @@ if command -v codesign &>/dev/null; then "${TVOS_DYLIBS_DIR}"/*.dylib \ "${TCP_DYLIBS_DIR}"/*.dylib \ "${IOS_BIN_DIR}/ax-service" \ + "${IOS_BIN_DIR}/argent-device-auth" \ "${IOS_BIN_DIR}/tvos-ax-service" \ "${IOS_BIN_DIR}/tvos-hid-daemon" \ "${TCP_BIN_DIR}/ax-service"; do