agent-device 0.14.7 → 0.14.9
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 +119 -9
- package/android-snapshot-helper/README.md +4 -2
- package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.14.7.apk → agent-device-android-snapshot-helper-0.14.9.apk} +0 -0
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.14.9.apk.sha256 +1 -0
- package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.14.7.manifest.json → agent-device-android-snapshot-helper-0.14.9.manifest.json} +6 -6
- package/dist/src/180.js +1 -1
- package/dist/src/208.js +1 -0
- package/dist/src/221.js +3 -3
- package/dist/src/6108.js +26 -0
- package/dist/src/7462.js +1 -0
- package/dist/src/7719.js +1 -0
- package/dist/src/9542.js +2 -2
- package/dist/src/9639.js +2 -2
- package/dist/src/9671.js +1 -0
- package/dist/src/9818.js +1 -1
- package/dist/src/android-adb.d.ts +11 -2
- package/dist/src/android-snapshot-helper.d.ts +12 -2
- package/dist/src/cli.js +82 -0
- package/dist/src/command-schema.js +382 -0
- package/dist/src/contracts.d.ts +1 -0
- package/dist/src/finders.d.ts +1 -0
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.js +1 -1
- package/dist/src/internal/bin.js +2 -461
- package/dist/src/internal/daemon.js +20 -20
- package/dist/src/io.js +1 -1
- package/dist/src/remote-config.js +1 -1
- package/dist/src/selectors.d.ts +1 -0
- package/dist/src/server.js +20 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +86 -13
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +160 -93
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +1 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +3 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +15 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SystemModal.swift +1 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TvRemote.swift +185 -0
- package/package.json +33 -6
- package/server.json +21 -0
- package/skills/agent-device/SKILL.md +11 -1
- package/skills/dogfood/SKILL.md +3 -1
- package/smithery.yaml +1 -0
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.14.7.apk.sha256 +0 -1
- package/dist/src/2007.js +0 -26
- package/skills/react-devtools/SKILL.md +0 -48
package/dist/src/io.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export{createLocalArtifactAdapter}from"./7719.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export{resolveRemoteConfigPath,resolveRemoteConfigProfile}from"./208.js";
|
package/dist/src/selectors.d.ts
CHANGED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import{buildCommandUsageText as e,buildUsageText as t}from"./command-schema.js";import{readVersion as r}from"./9671.js";let n="agent-device",o=["workflow","debugging","react-devtools","remote","macos","dogfood"];function i(e={}){let t=!1===e.global?"npx -y agent-device mcp":"agent-device mcp",r=!1===e.global?"No global install required.":"npm install -g agent-device",n=e.client?`
|
|
2
|
+
Client hint: ${e.client}`:"";return`${r}
|
|
3
|
+
|
|
4
|
+
MCP server command:
|
|
5
|
+
${t}
|
|
6
|
+
|
|
7
|
+
Generic MCP JSON:
|
|
8
|
+
{
|
|
9
|
+
"mcpServers": {
|
|
10
|
+
"agent-device": {
|
|
11
|
+
"command": "${!1===e.global?"npx":"agent-device"}",
|
|
12
|
+
"args": ${JSON.stringify(!1===e.global?["-y","agent-device","mcp"]:["mcp"])}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
Use this server for discovery and routing only. For device actions, call the CLI commands returned by the help tool, starting with:
|
|
18
|
+
agent-device help workflow${n}
|
|
19
|
+
`}function a(r={}){if(r.topic&&r.command)throw Error("Provide either topic or command, not both.");let n=r.topic??r.command;if(!n)return t();if(r.topic&&!d(r.topic))throw Error(`Unknown help topic: ${r.topic}. Expected one of: ${o.join(", ")}`);let i=e(n);if(!i)throw Error(`Unknown command or help topic: ${n}`);return i}function s(){return[{uri:"agent-device://install",name:"agent-device MCP install",description:"Install and client configuration snippets.",mimeType:"text/markdown"},{uri:"agent-device://help",name:"agent-device command list",description:"Version-matched command list and global flags.",mimeType:"text/plain"},...o.map(e=>({uri:`agent-device://help/${e}`,name:`agent-device help ${e}`,description:`Version-matched ${e} workflow guidance.`,mimeType:"text/plain"}))]}function c(){return[l("agent-device-workflow","Plan a normal app automation loop."),l("agent-device-debugging","Collect focused debugging evidence."),l("agent-device-dogfood","Run exploratory QA with reproducible evidence."),l("agent-device-react-native-performance","Inspect React Native renders."),l("agent-device-macos","Inspect a macOS app or surface.")]}function l(e,t){return{name:e,description:t,arguments:[{name:"target",description:"Optional app, device, or task target.",required:!1}]}}function d(e){return o.includes(e)}function p(e){if("2.0"!==e.jsonrpc||"string"!=typeof e.method)return g(e.id??null,-32600,"Invalid JSON-RPC request.");if(void 0===e.id)return null;try{var l,p;return l=e.id,p=function(e,l){switch(e){case"initialize":return{protocolVersion:"2025-11-25",capabilities:{tools:{},resources:{},prompts:{}},serverInfo:{name:n,version:r()}};case"ping":return{};case"tools/list":return{tools:[{name:"status",description:"Report the installed agent-device MCP router and CLI metadata.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"install",description:"Return install and MCP client configuration snippets for agent-device.",inputSchema:{type:"object",properties:{client:{type:"string",description:"Optional client name for labeling the returned guidance."},global:{type:"boolean",description:"Use a global agent-device binary when true; use npx when false."}},additionalProperties:!1}},{name:"help",description:"Return version-matched CLI help for a workflow topic or command.",inputSchema:{type:"object",properties:{topic:{type:"string",enum:o,description:"Agent workflow topic."},command:{type:"string",description:"CLI command name such as snapshot, open, logs, or react-devtools."}},additionalProperties:!1}}]};case"tools/call":var p,g,w,y=l;let b=m(y),$=h(b,"name"),k=f(b.arguments);try{if("status"===$)return u(JSON.stringify({name:n,registryName:"io.github.callstackincubator/agent-device",version:r(),transport:"stdio",command:"agent-device mcp",node:process.version,capabilities:{tools:["status","install","help"],resources:s().map(e=>e.uri),prompts:c().map(e=>e.name)},note:"This MCP server routes agents to the agent-device CLI. It does not expose device automation or shell execution tools."},null,2));if("install"===$)return u(i(k));if("help"===$)return u(a(k));throw Error(`Unknown tool: ${$}`)}catch(e){return u(e instanceof Error?e.message:String(e),!0)}return;case"resources/list":return{resources:s()};case"resources/read":let x;return p=l,{contents:[{uri:x=h(m(p),"uri"),mimeType:"agent-device://install"===x?"text/markdown":"text/plain",text:function(e){if("agent-device://install"===e)return i();if("agent-device://help"===e)return t();let r=e.startsWith("agent-device://help/")?e.slice(20):"";if(d(r))return a({topic:r});throw Error(`Unknown resource: ${e}`)}(x)}]};case"prompts/list":return{prompts:c()};case"prompts/get":let C;return g=l,C=m(g),function(e,t={}){let r={"agent-device-workflow":"workflow","agent-device-debugging":"debugging","agent-device-dogfood":"dogfood","agent-device-react-native-performance":"react-devtools","agent-device-macos":"macos"}[e];if(!r)throw Error(`Unknown prompt: ${e}`);let n=t.target?` Target: ${t.target}.`:"";return{description:`Use agent-device help ${r} before planning commands.${n}`,messages:[{role:"user",content:{type:"text",text:`Read the agent-device ${r} guidance through the MCP help tool, then produce a concise command plan using agent-device CLI commands only.${n}`}}]}}(h(C,"name"),(w=C.arguments,Object.fromEntries(Object.entries(f(w)).filter(e=>"string"==typeof e[1]))));default:throw new v(`Unsupported MCP method: ${e}`)}}(e.method,e.params),{jsonrpc:"2.0",id:l,result:p}}catch(t){if(t instanceof v)return g(e.id,-32601,t.message);return g(e.id,-32602,t instanceof Error?t.message:String(t))}}function u(e,t=!1){return{isError:t,content:[{type:"text",text:e}]}}function g(e,t,r){return{jsonrpc:"2.0",id:e,error:{code:t,message:r}}}function m(e){if(!e||"object"!=typeof e||Array.isArray(e))throw Error("Expected object parameters.");return e}function f(e){return void 0===e?{}:m(e)}function h(e,t){let r=e[t];if("string"!=typeof r||0===r.length)throw Error(`Expected ${t} to be a non-empty string.`);return r}class v extends Error{}async function w(){let e=new y(e=>{let t=function(e){if(Array.isArray(e)){let t=e.flatMap(e=>{var t;return(t=p(e))?[t]:[]});return t.length>0?t:null}return p(e)}(e);t&&b(t)});process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{try{e.push(t)}catch(e){b({jsonrpc:"2.0",id:null,error:{code:-32700,message:e instanceof Error?e.message:String(e)}})}}),await new Promise(e=>{process.stdin.on("end",e),process.stdin.on("close",e),process.stdin.resume()})}class y{buffer="";sink;constructor(e){this.sink=e}push(e){for(this.buffer+=e;;){let e=this.tryReadLineMessage();if(void 0!==e){this.emit(e);continue}break}}tryReadLineMessage(){let e=this.buffer.indexOf("\n");if(-1===e)return;let t=this.buffer.slice(0,e).trim();return this.buffer=this.buffer.slice(e+1),t.length>0?t:void 0}emit(e){let t=JSON.parse(e);Array.isArray(t),this.sink(t)}}function b(e){process.stdout.write(`${JSON.stringify(e)}
|
|
20
|
+
`)}export{w as runAgentDeviceMcpServer};
|
package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift
CHANGED
|
@@ -13,6 +13,18 @@ extension RunnerTests {
|
|
|
13
13
|
return (gestureStartUptimeMs, currentUptimeMs())
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
private func unsupportedResponse(for outcome: RunnerInteractionOutcome) -> Response? {
|
|
17
|
+
switch outcome {
|
|
18
|
+
case .performed:
|
|
19
|
+
return nil
|
|
20
|
+
case .unsupported(let message):
|
|
21
|
+
return Response(
|
|
22
|
+
ok: false,
|
|
23
|
+
error: ErrorPayload(code: "UNSUPPORTED_OPERATION", message: message)
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
16
28
|
func execute(command: Command) throws -> Response {
|
|
17
29
|
if Thread.isMainThread {
|
|
18
30
|
return try executeOnMainSafely(command: command)
|
|
@@ -231,11 +243,15 @@ extension RunnerTests {
|
|
|
231
243
|
case .tap:
|
|
232
244
|
if let text = command.text {
|
|
233
245
|
if let element = findElement(app: activeApp, text: text) {
|
|
246
|
+
var outcome = RunnerInteractionOutcome.performed
|
|
234
247
|
let timing = measureGesture {
|
|
235
248
|
withTemporaryScrollIdleTimeoutIfSupported(activeApp) {
|
|
236
|
-
element
|
|
249
|
+
outcome = activateElement(app: activeApp, element: element, action: "tap by text")
|
|
237
250
|
}
|
|
238
251
|
}
|
|
252
|
+
if let response = unsupportedResponse(for: outcome) {
|
|
253
|
+
return response
|
|
254
|
+
}
|
|
239
255
|
return Response(
|
|
240
256
|
ok: true,
|
|
241
257
|
data: DataPayload(
|
|
@@ -249,11 +265,15 @@ extension RunnerTests {
|
|
|
249
265
|
}
|
|
250
266
|
if let x = command.x, let y = command.y {
|
|
251
267
|
let touchFrame = resolvedTouchVisualizationFrame(app: activeApp, x: x, y: y)
|
|
268
|
+
var outcome = RunnerInteractionOutcome.performed
|
|
252
269
|
let timing = measureGesture {
|
|
253
270
|
withTemporaryScrollIdleTimeoutIfSupported(activeApp) {
|
|
254
|
-
tapAt(app: activeApp, x: x, y: y)
|
|
271
|
+
outcome = tapAt(app: activeApp, x: x, y: y)
|
|
255
272
|
}
|
|
256
273
|
}
|
|
274
|
+
if let response = unsupportedResponse(for: outcome) {
|
|
275
|
+
return response
|
|
276
|
+
}
|
|
257
277
|
return Response(
|
|
258
278
|
ok: true,
|
|
259
279
|
data: DataPayload(
|
|
@@ -309,13 +329,19 @@ extension RunnerTests {
|
|
|
309
329
|
let doubleTap = command.doubleTap ?? false
|
|
310
330
|
let touchFrame = resolvedTouchVisualizationFrame(app: activeApp, x: x, y: y)
|
|
311
331
|
if doubleTap {
|
|
332
|
+
var outcome = RunnerInteractionOutcome.performed
|
|
312
333
|
let timing = measureGesture {
|
|
313
334
|
withTemporaryScrollIdleTimeoutIfSupported(activeApp) {
|
|
314
335
|
runSeries(count: count, pauseMs: intervalMs) { _ in
|
|
315
|
-
|
|
336
|
+
if case .performed = outcome {
|
|
337
|
+
outcome = doubleTapAt(app: activeApp, x: x, y: y)
|
|
338
|
+
}
|
|
316
339
|
}
|
|
317
340
|
}
|
|
318
341
|
}
|
|
342
|
+
if let response = unsupportedResponse(for: outcome) {
|
|
343
|
+
return response
|
|
344
|
+
}
|
|
319
345
|
return Response(
|
|
320
346
|
ok: true,
|
|
321
347
|
data: DataPayload(
|
|
@@ -329,13 +355,19 @@ extension RunnerTests {
|
|
|
329
355
|
)
|
|
330
356
|
)
|
|
331
357
|
}
|
|
358
|
+
var outcome = RunnerInteractionOutcome.performed
|
|
332
359
|
let timing = measureGesture {
|
|
333
360
|
withTemporaryScrollIdleTimeoutIfSupported(activeApp) {
|
|
334
361
|
runSeries(count: count, pauseMs: intervalMs) { _ in
|
|
335
|
-
|
|
362
|
+
if case .performed = outcome {
|
|
363
|
+
outcome = tapAt(app: activeApp, x: x, y: y)
|
|
364
|
+
}
|
|
336
365
|
}
|
|
337
366
|
}
|
|
338
367
|
}
|
|
368
|
+
if let response = unsupportedResponse(for: outcome) {
|
|
369
|
+
return response
|
|
370
|
+
}
|
|
339
371
|
return Response(
|
|
340
372
|
ok: true,
|
|
341
373
|
data: DataPayload(
|
|
@@ -354,11 +386,15 @@ extension RunnerTests {
|
|
|
354
386
|
}
|
|
355
387
|
let duration = (command.durationMs ?? 800) / 1000.0
|
|
356
388
|
let touchFrame = resolvedTouchVisualizationFrame(app: activeApp, x: x, y: y)
|
|
389
|
+
var outcome = RunnerInteractionOutcome.performed
|
|
357
390
|
let timing = measureGesture {
|
|
358
391
|
withTemporaryScrollIdleTimeoutIfSupported(activeApp) {
|
|
359
|
-
longPressAt(app: activeApp, x: x, y: y, duration: duration)
|
|
392
|
+
outcome = longPressAt(app: activeApp, x: x, y: y, duration: duration)
|
|
360
393
|
}
|
|
361
394
|
}
|
|
395
|
+
if let response = unsupportedResponse(for: outcome) {
|
|
396
|
+
return response
|
|
397
|
+
}
|
|
362
398
|
return Response(
|
|
363
399
|
ok: true,
|
|
364
400
|
data: DataPayload(
|
|
@@ -377,11 +413,15 @@ extension RunnerTests {
|
|
|
377
413
|
}
|
|
378
414
|
let holdDuration = min(max((command.durationMs ?? 60) / 1000.0, 0.016), 10.0)
|
|
379
415
|
let dragFrame = resolvedDragVisualizationFrame(app: activeApp, x: x, y: y, x2: x2, y2: y2)
|
|
416
|
+
var outcome = RunnerInteractionOutcome.performed
|
|
380
417
|
let timing = measureGesture {
|
|
381
418
|
withTemporaryScrollIdleTimeoutIfSupported(activeApp) {
|
|
382
|
-
dragAt(app: activeApp, x: x, y: y, x2: x2, y2: y2, holdDuration: holdDuration)
|
|
419
|
+
outcome = dragAt(app: activeApp, x: x, y: y, x2: x2, y2: y2, holdDuration: holdDuration)
|
|
383
420
|
}
|
|
384
421
|
}
|
|
422
|
+
if let response = unsupportedResponse(for: outcome) {
|
|
423
|
+
return response
|
|
424
|
+
}
|
|
385
425
|
return Response(
|
|
386
426
|
ok: true,
|
|
387
427
|
data: DataPayload(
|
|
@@ -407,18 +447,25 @@ extension RunnerTests {
|
|
|
407
447
|
return Response(ok: false, error: ErrorPayload(message: "dragSeries pattern must be one-way or ping-pong"))
|
|
408
448
|
}
|
|
409
449
|
let holdDuration = min(max((command.durationMs ?? 60) / 1000.0, 0.016), 10.0)
|
|
450
|
+
var outcome = RunnerInteractionOutcome.performed
|
|
410
451
|
let timing = measureGesture {
|
|
411
452
|
withTemporaryScrollIdleTimeoutIfSupported(activeApp) {
|
|
412
453
|
runSeries(count: count, pauseMs: pauseMs) { idx in
|
|
454
|
+
guard case .performed = outcome else {
|
|
455
|
+
return
|
|
456
|
+
}
|
|
413
457
|
let reverse = pattern == "ping-pong" && (idx % 2 == 1)
|
|
414
458
|
if reverse {
|
|
415
|
-
dragAt(app: activeApp, x: x2, y: y2, x2: x, y2: y, holdDuration: holdDuration)
|
|
459
|
+
outcome = dragAt(app: activeApp, x: x2, y: y2, x2: x, y2: y, holdDuration: holdDuration)
|
|
416
460
|
} else {
|
|
417
|
-
dragAt(app: activeApp, x: x, y: y, x2: x2, y2: y2, holdDuration: holdDuration)
|
|
461
|
+
outcome = dragAt(app: activeApp, x: x, y: y, x2: x2, y2: y2, holdDuration: holdDuration)
|
|
418
462
|
}
|
|
419
463
|
}
|
|
420
464
|
}
|
|
421
465
|
}
|
|
466
|
+
if let response = unsupportedResponse(for: outcome) {
|
|
467
|
+
return response
|
|
468
|
+
}
|
|
422
469
|
return Response(
|
|
423
470
|
ok: true,
|
|
424
471
|
data: DataPayload(
|
|
@@ -427,6 +474,18 @@ extension RunnerTests {
|
|
|
427
474
|
gestureEndUptimeMs: timing.gestureEndUptimeMs
|
|
428
475
|
)
|
|
429
476
|
)
|
|
477
|
+
case .remotePress:
|
|
478
|
+
guard let button = tvRemoteButton(from: command.remoteButton) else {
|
|
479
|
+
return Response(ok: false, error: ErrorPayload(message: "remotePress requires remoteButton"))
|
|
480
|
+
}
|
|
481
|
+
let duration = (command.durationMs ?? 0) / 1000.0
|
|
482
|
+
guard pressTvRemote(button, duration: duration) else {
|
|
483
|
+
return Response(
|
|
484
|
+
ok: false,
|
|
485
|
+
error: ErrorPayload(code: "UNSUPPORTED_OPERATION", message: "remotePress is only supported on tvOS")
|
|
486
|
+
)
|
|
487
|
+
}
|
|
488
|
+
return Response(ok: true, data: DataPayload(message: "remote pressed"))
|
|
430
489
|
case .type:
|
|
431
490
|
guard let text = command.text else {
|
|
432
491
|
return Response(ok: false, error: ErrorPayload(message: "type requires text"))
|
|
@@ -633,13 +692,23 @@ extension RunnerTests {
|
|
|
633
692
|
return Response(ok: false, error: ErrorPayload(message: "alert not found"))
|
|
634
693
|
}
|
|
635
694
|
if action == "accept" {
|
|
636
|
-
let button = alert.buttons.allElementsBoundByIndex.first
|
|
637
|
-
|
|
695
|
+
guard let button = alert.buttons.allElementsBoundByIndex.first else {
|
|
696
|
+
return Response(ok: false, error: ErrorPayload(message: "alert accept button not found"))
|
|
697
|
+
}
|
|
698
|
+
let outcome = activateElement(app: activeApp, element: button, action: "alert accept")
|
|
699
|
+
if let response = unsupportedResponse(for: outcome) {
|
|
700
|
+
return response
|
|
701
|
+
}
|
|
638
702
|
return Response(ok: true, data: DataPayload(message: "accepted"))
|
|
639
703
|
}
|
|
640
704
|
if action == "dismiss" {
|
|
641
|
-
let button = alert.buttons.allElementsBoundByIndex.last
|
|
642
|
-
|
|
705
|
+
guard let button = alert.buttons.allElementsBoundByIndex.last else {
|
|
706
|
+
return Response(ok: false, error: ErrorPayload(message: "alert dismiss button not found"))
|
|
707
|
+
}
|
|
708
|
+
let outcome = activateElement(app: activeApp, element: button, action: "alert dismiss")
|
|
709
|
+
if let response = unsupportedResponse(for: outcome) {
|
|
710
|
+
return response
|
|
711
|
+
}
|
|
643
712
|
return Response(ok: true, data: DataPayload(message: "dismissed"))
|
|
644
713
|
}
|
|
645
714
|
let buttonLabels = alert.buttons.allElementsBoundByIndex.map { $0.label }
|
|
@@ -648,8 +717,12 @@ extension RunnerTests {
|
|
|
648
717
|
guard let scale = command.scale, scale > 0 else {
|
|
649
718
|
return Response(ok: false, error: ErrorPayload(message: "pinch requires scale > 0"))
|
|
650
719
|
}
|
|
720
|
+
var outcome = RunnerInteractionOutcome.performed
|
|
651
721
|
let timing = measureGesture {
|
|
652
|
-
pinch(app: activeApp, scale: scale, x: command.x, y: command.y)
|
|
722
|
+
outcome = pinch(app: activeApp, scale: scale, x: command.x, y: command.y)
|
|
723
|
+
}
|
|
724
|
+
if let response = unsupportedResponse(for: outcome) {
|
|
725
|
+
return response
|
|
653
726
|
}
|
|
654
727
|
return Response(
|
|
655
728
|
ok: true,
|