agent-device 0.16.9 → 0.16.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.16.9.apk → agent-device-android-multitouch-helper-0.16.11.apk} +0 -0
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.16.11.apk.sha256 +1 -0
- package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.16.9.manifest.json → agent-device-android-multitouch-helper-0.16.11.manifest.json} +4 -4
- package/android-snapshot-helper/README.md +6 -0
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.11.apk +0 -0
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.11.apk.sha256 +1 -0
- package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.16.9.manifest.json → agent-device-android-snapshot-helper-0.16.11.manifest.json} +6 -6
- package/dist/src/1352.js +1 -1
- package/dist/src/221.js +6 -6
- package/dist/src/2415.js +27 -27
- package/dist/src/2805.js +1 -1
- package/dist/src/4778.js +1 -0
- package/dist/src/5792.js +1 -1
- package/dist/src/6085.js +1 -1
- package/dist/src/6232.js +1 -1
- package/dist/src/8699.js +1 -1
- package/dist/src/9238.js +4 -0
- package/dist/src/9533.js +1 -1
- package/dist/src/9542.js +3 -3
- package/dist/src/apple.js +1 -1
- package/dist/src/apps.js +2 -2
- package/dist/src/args.js +54 -25
- package/dist/src/batch.d.ts +1 -0
- package/dist/src/cli.js +19 -19
- package/dist/src/command-metadata.js +1 -1
- package/dist/src/command-surface.js +1 -1
- package/dist/src/contracts.d.ts +1 -0
- package/dist/src/contracts.js +1 -1
- package/dist/src/generic.js +10 -10
- package/dist/src/index.d.ts +9 -0
- package/dist/src/input-actions.js +1 -1
- package/dist/src/record-trace.js +3 -3
- package/dist/src/session.js +9 -9
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h +7 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +109 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +282 -226
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +282 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Exceptions.swift +29 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +44 -34
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +41 -1
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +2 -20
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SystemModal.swift +10 -50
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +3 -23
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +64 -22
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +7 -4
- package/package.json +1 -1
- package/server.json +2 -2
- package/skills/dogfood/SKILL.md +1 -1
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.16.9.apk.sha256 +0 -1
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.9.apk +0 -0
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.9.apk.sha256 +0 -1
- package/dist/src/2842.js +0 -1
- package/dist/src/8114.js +0 -4
|
@@ -73,8 +73,7 @@ extension RunnerTests {
|
|
|
73
73
|
// under XCUITest, so text entry readiness is driven by tap/keyboard state.
|
|
74
74
|
return nil
|
|
75
75
|
#else
|
|
76
|
-
|
|
77
|
-
let exceptionMessage = RunnerObjCExceptionCatcher.catchException({
|
|
76
|
+
return safely("FOCUSED_INPUT_QUERY") {
|
|
78
77
|
let candidates = app
|
|
79
78
|
.descendants(matching: .any)
|
|
80
79
|
.matching(NSPredicate(format: "hasKeyboardFocus == 1"))
|
|
@@ -82,21 +81,13 @@ extension RunnerTests {
|
|
|
82
81
|
for candidate in candidates where candidate.exists {
|
|
83
82
|
switch candidate.elementType {
|
|
84
83
|
case .textField, .secureTextField, .searchField, .textView:
|
|
85
|
-
|
|
86
|
-
return
|
|
84
|
+
return candidate
|
|
87
85
|
default:
|
|
88
86
|
continue
|
|
89
87
|
}
|
|
90
88
|
}
|
|
91
|
-
})
|
|
92
|
-
if let exceptionMessage {
|
|
93
|
-
NSLog(
|
|
94
|
-
"AGENT_DEVICE_RUNNER_FOCUSED_INPUT_QUERY_IGNORED_EXCEPTION=%@",
|
|
95
|
-
exceptionMessage
|
|
96
|
-
)
|
|
97
89
|
return nil
|
|
98
90
|
}
|
|
99
|
-
return focused
|
|
100
91
|
#endif
|
|
101
92
|
}
|
|
102
93
|
|
|
@@ -650,18 +641,7 @@ extension RunnerTests {
|
|
|
650
641
|
|
|
651
642
|
private func keyboardElementExists(app: XCUIApplication) -> Bool {
|
|
652
643
|
#if os(iOS)
|
|
653
|
-
|
|
654
|
-
let exceptionMessage = RunnerObjCExceptionCatcher.catchException({
|
|
655
|
-
exists = app.keyboards.firstMatch.exists
|
|
656
|
-
})
|
|
657
|
-
if let exceptionMessage {
|
|
658
|
-
NSLog(
|
|
659
|
-
"AGENT_DEVICE_RUNNER_KEYBOARD_EXISTS_IGNORED_EXCEPTION=%@",
|
|
660
|
-
exceptionMessage
|
|
661
|
-
)
|
|
662
|
-
return false
|
|
663
|
-
}
|
|
664
|
-
return exists
|
|
644
|
+
return safely("KEYBOARD_EXISTS", false) { app.keyboards.firstMatch.exists }
|
|
665
645
|
#else
|
|
666
646
|
return false
|
|
667
647
|
#endif
|
|
@@ -19,7 +19,11 @@ extension RunnerTests {
|
|
|
19
19
|
if buffer.count + data.count > self.maxRequestBytes {
|
|
20
20
|
let response = self.jsonResponse(
|
|
21
21
|
status: 413,
|
|
22
|
-
response:
|
|
22
|
+
response: self.errorResponse(
|
|
23
|
+
code: "INVALID_ARGS",
|
|
24
|
+
message: "runner request body exceeds \(self.maxRequestBytes) bytes",
|
|
25
|
+
hint: "Send one runner command per request and keep the payload below the runner request limit."
|
|
26
|
+
)
|
|
23
27
|
)
|
|
24
28
|
self.sendResponse(response, over: connection) { [weak self] in
|
|
25
29
|
self?.finish()
|
|
@@ -28,10 +32,11 @@ extension RunnerTests {
|
|
|
28
32
|
}
|
|
29
33
|
let combined = buffer + data
|
|
30
34
|
if let body = self.parseRequest(data: combined) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
self.handleRequestBody(body) { [weak self] result in
|
|
36
|
+
self?.sendResponse(result.data, over: connection) { [weak self] in
|
|
37
|
+
if result.shouldFinish {
|
|
38
|
+
self?.finish()
|
|
39
|
+
}
|
|
35
40
|
}
|
|
36
41
|
}
|
|
37
42
|
} else {
|
|
@@ -82,29 +87,62 @@ extension RunnerTests {
|
|
|
82
87
|
return nil
|
|
83
88
|
}
|
|
84
89
|
|
|
85
|
-
private func handleRequestBody(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
90
|
+
private func handleRequestBody(
|
|
91
|
+
_ body: Data,
|
|
92
|
+
completion: @escaping ((data: Data, shouldFinish: Bool)) -> Void
|
|
93
|
+
) {
|
|
94
|
+
guard String(data: body, encoding: .utf8) != nil else {
|
|
95
|
+
completion((
|
|
96
|
+
jsonResponse(
|
|
97
|
+
status: 400,
|
|
98
|
+
response: errorResponse(
|
|
99
|
+
code: "INVALID_ARGS",
|
|
100
|
+
message: "runner request body must be UTF-8 JSON",
|
|
101
|
+
hint: "Send a JSON object matching the runner command protocol."
|
|
102
|
+
)
|
|
103
|
+
),
|
|
95
104
|
false
|
|
96
|
-
)
|
|
105
|
+
))
|
|
106
|
+
return
|
|
97
107
|
}
|
|
98
108
|
|
|
99
109
|
do {
|
|
100
|
-
let command = try JSONDecoder().decode(Command.self, from:
|
|
101
|
-
|
|
102
|
-
|
|
110
|
+
let command = try JSONDecoder().decode(Command.self, from: body)
|
|
111
|
+
if command.command == .status {
|
|
112
|
+
completion((jsonResponse(status: 200, response: executeStatus(command: command)), false))
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
commandJournal.accept(command: command)
|
|
116
|
+
commandExecutionQueue.async {
|
|
117
|
+
do {
|
|
118
|
+
let response = try self.executeAccepted(command: command)
|
|
119
|
+
completion((self.jsonResponse(status: 200, response: response), command.command == .shutdown))
|
|
120
|
+
} catch {
|
|
121
|
+
completion((
|
|
122
|
+
self.jsonResponse(
|
|
123
|
+
status: 500,
|
|
124
|
+
response: self.errorResponse(
|
|
125
|
+
code: "COMMAND_FAILED",
|
|
126
|
+
message: error.localizedDescription,
|
|
127
|
+
hint: "Check the runner log for XCTest details, then retry after the app is foregrounded if this was a timeout or activation failure."
|
|
128
|
+
)
|
|
129
|
+
),
|
|
130
|
+
false
|
|
131
|
+
))
|
|
132
|
+
}
|
|
133
|
+
}
|
|
103
134
|
} catch {
|
|
104
|
-
|
|
105
|
-
jsonResponse(
|
|
135
|
+
completion((
|
|
136
|
+
jsonResponse(
|
|
137
|
+
status: 400,
|
|
138
|
+
response: errorResponse(
|
|
139
|
+
code: "INVALID_ARGS",
|
|
140
|
+
message: "runner command payload is invalid: \(String(describing: error))",
|
|
141
|
+
hint: "Check the command name and fields against the runner protocol."
|
|
142
|
+
)
|
|
143
|
+
),
|
|
106
144
|
false
|
|
107
|
-
)
|
|
145
|
+
))
|
|
108
146
|
}
|
|
109
147
|
}
|
|
110
148
|
|
|
@@ -116,6 +154,10 @@ extension RunnerTests {
|
|
|
116
154
|
return httpResponse(status: status, body: body)
|
|
117
155
|
}
|
|
118
156
|
|
|
157
|
+
private func errorResponse(code: String, message: String, hint: String? = nil) -> Response {
|
|
158
|
+
Response(ok: false, error: ErrorPayload(code: code, message: message, hint: hint))
|
|
159
|
+
}
|
|
160
|
+
|
|
119
161
|
private func httpResponse(status: Int, body: String) -> Data {
|
|
120
162
|
let headers = [
|
|
121
163
|
"HTTP/1.1 \(status) OK",
|
|
@@ -32,6 +32,8 @@ final class RunnerTests: XCTestCase {
|
|
|
32
32
|
static let defaultRecordingFps: Int32 = 15
|
|
33
33
|
var listener: NWListener?
|
|
34
34
|
var doneExpectation: XCTestExpectation?
|
|
35
|
+
let transportQueue = DispatchQueue(label: "agent-device.runner.transport")
|
|
36
|
+
let commandExecutionQueue = DispatchQueue(label: "agent-device.runner.commands")
|
|
35
37
|
let app = XCUIApplication()
|
|
36
38
|
lazy var springboard = XCUIApplication(bundleIdentifier: Self.springboardBundleId)
|
|
37
39
|
var currentApp: XCUIApplication?
|
|
@@ -53,6 +55,7 @@ final class RunnerTests: XCTestCase {
|
|
|
53
55
|
var needsPostSnapshotInteractionDelay = false
|
|
54
56
|
var needsFirstInteractionDelay = false
|
|
55
57
|
var activeRecording: ScreenRecorder?
|
|
58
|
+
let commandJournal = RunnerCommandJournal()
|
|
56
59
|
let interactiveTypes: Set<XCUIElement.ElementType> = [
|
|
57
60
|
.button,
|
|
58
61
|
.cell,
|
|
@@ -91,7 +94,6 @@ final class RunnerTests: XCTestCase {
|
|
|
91
94
|
func testCommand() throws {
|
|
92
95
|
doneExpectation = expectation(description: "agent-device command handled")
|
|
93
96
|
NSLog("AGENT_DEVICE_RUNNER_HEADLESS_STARTUP=1")
|
|
94
|
-
let queue = DispatchQueue(label: "agent-device.runner")
|
|
95
97
|
let desiredPort = RunnerEnv.resolvePort()
|
|
96
98
|
NSLog("AGENT_DEVICE_RUNNER_DESIRED_PORT=%d", desiredPort)
|
|
97
99
|
listener = try makeRunnerListener(desiredPort: desiredPort)
|
|
@@ -112,10 +114,11 @@ final class RunnerTests: XCTestCase {
|
|
|
112
114
|
}
|
|
113
115
|
}
|
|
114
116
|
listener?.newConnectionHandler = { [weak self] conn in
|
|
115
|
-
|
|
116
|
-
|
|
117
|
+
guard let self else { return }
|
|
118
|
+
conn.start(queue: self.transportQueue)
|
|
119
|
+
self.handle(connection: conn)
|
|
117
120
|
}
|
|
118
|
-
listener?.start(queue:
|
|
121
|
+
listener?.start(queue: transportQueue)
|
|
119
122
|
|
|
120
123
|
guard let expectation = doneExpectation else {
|
|
121
124
|
XCTFail("runner expectation was not initialized")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-device",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.11",
|
|
4
4
|
"description": "Agent-native CLI for AI mobile testing and app automation across iOS, Android, tvOS, Android TV, macOS, and Linux.",
|
|
5
5
|
"mcpName": "io.github.callstackincubator/agent-device",
|
|
6
6
|
"license": "MIT",
|
package/server.json
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"url": "https://github.com/callstackincubator/agent-device",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "0.16.
|
|
10
|
+
"version": "0.16.11",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "agent-device",
|
|
15
|
-
"version": "0.16.
|
|
15
|
+
"version": "0.16.11",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
}
|
package/skills/dogfood/SKILL.md
CHANGED
|
@@ -22,6 +22,6 @@ Read current CLI guidance:
|
|
|
22
22
|
agent-device help dogfood
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
Loop: open
|
|
25
|
+
Loop: open app -> snapshot -i + screenshot -> explore flows -> capture evidence per issue -> close.
|
|
26
26
|
|
|
27
27
|
Target app is required; infer platform or ask. Findings must come from runtime behavior, not source reads. Let `help dogfood` provide exact report shape, evidence commands, and current workflow guidance.
|
package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.16.9.apk.sha256
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
5e33b9281e0c508ae6605bcd6766a859467939aae2e9fde22414a37e0f577602 agent-device-android-multitouch-helper-0.16.9.apk
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
5d1106a1696ba569c2497666e80f9e2f0ba8d44072613a4491242e1118885f1a agent-device-android-snapshot-helper-0.16.9.apk
|
package/dist/src/2842.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
function e(e){let l=`${e??""}`.toLowerCase();return l.includes("scroll")||l.includes("recyclerview")||l.includes("listview")||l.includes("gridview")||l.includes("collectionview")||"table"===l}function l(l){return!!e(l.type)||`${l.role??""} ${l.subrole??""}`.toLowerCase().includes("scroll")}export{l as isScrollableNodeLike,e as isScrollableType};
|
package/dist/src/8114.js
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import e from"node:path";import t from"node:crypto";import n from"node:fs";import{resolveUserPath as r,expandUserHomePath as o}from"./3267.js";import{findProjectRoot as s}from"./9671.js";function i(e,t){let n=[],r=e+t.toString(),o=r.indexOf("\n");for(;-1!==o;){let e=r.slice(0,o).trim();r=r.slice(o+1),e&&n.push(e),o=r.indexOf("\n")}return{lines:n,buffer:r}}function a(t){let n,s=(n=(t??"").trim())?r(n):e.join(o("~"),".agent-device");return{baseDir:s,infoPath:e.join(s,"daemon.json"),lockPath:e.join(s,"daemon.lock"),logPath:e.join(s,"daemon.log"),sessionsDir:e.join(s,"sessions")}}function l(e){let t=(e??"").trim().toLowerCase();return"http"===t?"http":"dual"===t?"dual":"socket"}function u(e){let t=(e??"").trim().toLowerCase();return"auto"===t?"auto":"socket"===t?"socket":"http"===t?"http":"auto"}function p(e){return"tenant"===(e??"").trim().toLowerCase()?"tenant":"none"}function f(e){if(!e)return;let t=e.trim();if(t&&/^[a-zA-Z0-9._-]{1,128}$/.test(t))return t}let m=/(?:^|[^\w$.])(?:import|export)\s+(?:type\s+)?(?:[^'"`]*?\s+from\s+)?['"]([^'"]+)['"]/gm,c=/import\(\s*['"]([^'"]+)['"]\s*\)/gm,d=[".ts",".tsx",".js",".jsx",".mjs",".cjs"];function $(){let e=process.argv[1];return e?g(e):"unknown"}function g(r,o=s()){try{let s=e.resolve(o),i=[e.resolve(r)],a=new Set,l=[];for(;i.length>0;){let t=i.pop();if(!t||a.has(t))continue;a.add(t);let r=n.statSync(t);if(!r.isFile())continue;let o=e.relative(s,t)||t;l.push(`${o}:${r.size}:${Math.trunc(r.mtimeMs)}`);let u=n.readFileSync(t,"utf8");for(let n of function(e){let t=new Set;return h(e,m,t),h(e,c,t),[...t]}(u)){let r=function(t,n){let r=e.resolve(e.dirname(t),n),o=y(r);if(o)return o;for(let e of d){let t=y(`${r}${e}`);if(t)return t}for(let t of d){let n=y(e.join(r,`index${t}`));if(n)return n}return null}(t,n);r&&i.push(r)}}let u=l.sort().join("|"),p=t.createHash("sha1").update(u).digest("hex");return`graph:${l.length}:${p}`}catch{return"unknown"}}function h(e,t,n){t.lastIndex=0;let r=null;for(;null!==(r=t.exec(e));){let e=r[1]?.trim();e?.startsWith(".")&&n.add(e)}}function y(e){try{return n.statSync(e).isFile()?e:null}catch{return null}}function v(e){return e.meta?.requestProgress==="replay-test"}function S(e){return!!e&&"object"==typeof e&&"progress"===e.type&&!!e.event}function x(e){return!!e&&"object"==typeof e&&"response"===e.type&&!!e.response}function j(e){return`${JSON.stringify({type:"progress",event:e})}
|
|
2
|
-
`}function P(e){return`${JSON.stringify({type:"response",response:e})}
|
|
3
|
-
`}function D(e){return`${JSON.stringify({type:"response",response:e})}
|
|
4
|
-
`}function _(t){var n,r,o;let s,i,a;if("replay-test"!==t.type)return;let l=(n=t,(s=n.title?.trim())?JSON.stringify(s):e.basename(n.file)),u=void 0!==t.durationMs?` (${a=(i=Math.max(0,(r=t).durationMs??0)/1e3)>=10?`${i.toFixed(1)}s`:i>=1?`${i.toFixed(2)}s`:`${i.toFixed(3).replace(/0+$/,"").replace(/\.$/,"")}s`,r.attempt&&r.attempt>1&&!r.retrying?`total ${a}`:a})`:"",p=void 0===(o=t).attempt?"":"fail"===o.status&&o.retrying&&void 0!==o.maxAttempts?` attempt ${o.attempt}/${o.maxAttempts} retrying`:o.attempt>1?` after ${o.attempt} attempts`:"",f=t.message?.replace(/\s+/g," ").trim();return"pass"===t.status?`PASS ${l}${p}${u}`:"skip"===t.status?[`SKIP ${l}`,f?` ${f}`:""].filter(Boolean).join("\n"):[`FAIL ${l}${p}${u}`,f?` ${f}`:"",t.artifactsDir?` artifacts: ${t.artifactsDir}`:""].filter(Boolean).join("\n")}export{g as computeDaemonCodeSignature,i as consumeTextLines,_ as formatRequestProgressEvent,S as isDaemonProgressEnvelope,x as isDaemonResponseEnvelope,f as normalizeTenantId,$ as resolveDaemonCodeSignature,a as resolveDaemonPaths,l as resolveDaemonServerMode,u as resolveDaemonTransportPreference,p as resolveSessionIsolationMode,j as serializeDaemonProgressEnvelope,P as serializeDaemonResponseEnvelope,D as serializeDaemonRpcResponseEnvelope,v as shouldStreamRequestProgress};
|