Skip to content

Commit 6380956

Browse files
authored
Merge pull request #1825 from web-infra-dev/claude/fix-adb-parameters-llROO
fix(core): fix Launch and runAdbShell parameter types
2 parents 5b1e052 + b4eb9a4 commit 6380956

6 files changed

Lines changed: 366 additions & 22 deletions

File tree

‎packages/android/src/agent.ts‎

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,6 @@ type WrappedAction<T extends DeviceAction> = (
3535
) => Promise<ActionReturn<T>>;
3636

3737
export class AndroidAgent extends PageAgent<AndroidDevice> {
38-
/**
39-
* Launch an Android app or URL
40-
*/
41-
launch!: WrappedAction<DeviceActionLaunch>;
42-
43-
/**
44-
* Execute ADB shell command on Android device
45-
*/
46-
runAdbShell!: WrappedAction<DeviceActionRunAdbShell>;
47-
4838
/**
4939
* Trigger the system back operation on Android devices
5040
*/
@@ -77,9 +67,6 @@ export class AndroidAgent extends PageAgent<AndroidDevice> {
7767
// Set the mapping on the device instance
7868
device.setAppNameMapping(this.appNameMapping);
7969

80-
this.launch = this.createActionWrapper<DeviceActionLaunch>('Launch');
81-
this.runAdbShell =
82-
this.createActionWrapper<DeviceActionRunAdbShell>('RunAdbShell');
8370
this.back =
8471
this.createActionWrapper<DeviceActionAndroidBackButton>(
8572
'AndroidBackButton',
@@ -94,6 +81,25 @@ export class AndroidAgent extends PageAgent<AndroidDevice> {
9481
);
9582
}
9683

84+
/**
85+
* Launch an Android app or URL
86+
* @param uri - App package name, URL, or app name to launch
87+
*/
88+
async launch(uri: string): Promise<void> {
89+
const action = this.wrapActionInActionSpace<DeviceActionLaunch>('Launch');
90+
return action({ uri });
91+
}
92+
93+
/**
94+
* Execute ADB shell command on Android device
95+
* @param command - ADB shell command to execute
96+
*/
97+
async runAdbShell(command: string): Promise<string> {
98+
const action =
99+
this.wrapActionInActionSpace<DeviceActionRunAdbShell>('RunAdbShell');
100+
return action({ command });
101+
}
102+
97103
private createActionWrapper<T extends DeviceAction>(
98104
name: string,
99105
): WrappedAction<T> {

‎packages/android/src/device.ts‎

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1621,13 +1621,13 @@ ${Object.keys(size)
16211621
* Platform-specific action definitions for Android
16221622
* Single source of truth for both runtime behavior and type definitions
16231623
*/
1624-
const runAdbShellParamSchema = z
1625-
.string()
1626-
.describe('ADB shell command to execute');
1624+
const runAdbShellParamSchema = z.object({
1625+
command: z.string().describe('ADB shell command to execute'),
1626+
});
16271627

1628-
const launchParamSchema = z
1629-
.string()
1630-
.describe('App package name or URL or app name to launch,');
1628+
const launchParamSchema = z.object({
1629+
uri: z.string().describe('App package name or URL or app name to launch'),
1630+
});
16311631

16321632
type RunAdbShellParam = z.infer<typeof runAdbShellParamSchema>;
16331633
type LaunchParam = z.infer<typeof launchParamSchema>;
@@ -1651,8 +1651,11 @@ const createPlatformActions = (
16511651
interfaceAlias: 'runAdbShell',
16521652
paramSchema: runAdbShellParamSchema,
16531653
call: async (param) => {
1654+
if (!param.command || param.command.trim() === '') {
1655+
throw new Error('RunAdbShell requires a non-empty command parameter');
1656+
}
16541657
const adb = await device.getAdb();
1655-
return await adb.shell(param);
1658+
return await adb.shell(param.command);
16561659
},
16571660
}),
16581661
Launch: defineAction({
@@ -1661,7 +1664,10 @@ const createPlatformActions = (
16611664
interfaceAlias: 'launch',
16621665
paramSchema: launchParamSchema,
16631666
call: async (param) => {
1664-
await device.launch(param);
1667+
if (!param.uri || param.uri.trim() === '') {
1668+
throw new Error('Launch requires a non-empty uri parameter');
1669+
}
1670+
await device.launch(param.uri);
16651671
},
16661672
}),
16671673
AndroidBackButton: defineAction({

‎packages/android/tests/unit-test/agent.test.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ describe('AndroidAgent', () => {
107107

108108
await agent.launch(uri);
109109

110-
expect(launchSpy).toHaveBeenCalledWith(uri);
110+
// agent.launch(uri) converts string to { uri } object before calling device action
111+
expect(launchSpy).toHaveBeenCalledWith({ uri });
111112
});
112113
});
113114

‎packages/core/src/yaml/player.ts‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,40 @@ export class ScriptPlayer<T extends MidsceneYamlScriptEnv> {
534534
);
535535

536536
// Store result if there's a name property in flowItem
537+
const resultName = (flowItem as any).name;
538+
if (result !== undefined) {
539+
this.setResult(resultName, result);
540+
}
541+
} else if (
542+
typeof actionParamForMatchedAction === 'string' &&
543+
(matchedAction.name === 'Launch' ||
544+
matchedAction.interfaceAlias === 'launch') &&
545+
typeof (agent as any).launch === 'function'
546+
) {
547+
// Call agent.launch directly for Launch action with string param
548+
debug(`Calling agent.launch with: ${actionParamForMatchedAction}`);
549+
const result = await (agent as any).launch(
550+
actionParamForMatchedAction,
551+
);
552+
553+
const resultName = (flowItem as any).name;
554+
if (result !== undefined) {
555+
this.setResult(resultName, result);
556+
}
557+
} else if (
558+
typeof actionParamForMatchedAction === 'string' &&
559+
(matchedAction.name === 'RunAdbShell' ||
560+
matchedAction.interfaceAlias === 'runAdbShell') &&
561+
typeof (agent as any).runAdbShell === 'function'
562+
) {
563+
// Call agent.runAdbShell directly for RunAdbShell action with string param
564+
debug(
565+
`Calling agent.runAdbShell with: ${actionParamForMatchedAction}`,
566+
);
567+
const result = await (agent as any).runAdbShell(
568+
actionParamForMatchedAction,
569+
);
570+
537571
const resultName = (flowItem as any).name;
538572
if (result !== undefined) {
539573
this.setResult(resultName, result);

‎packages/core/tests/unit-test/prompt/prompt.test.ts‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,46 @@ describe('action space', () => {
113113
- option?: number // An optional option value"
114114
`);
115115
});
116+
117+
it('action with object param schema (Launch-like)', () => {
118+
const action = descriptionForAction(
119+
{
120+
name: 'Launch',
121+
description: 'Launch an app or URL',
122+
paramSchema: z.object({
123+
uri: z.string().describe('The URI to launch'),
124+
}),
125+
call: async () => {},
126+
},
127+
mockLocatorScheme,
128+
);
129+
expect(action).toMatchInlineSnapshot(`
130+
"- Launch, Launch an app or URL
131+
- type: "Launch"
132+
- param:
133+
- uri: string // The URI to launch"
134+
`);
135+
});
136+
137+
it('action with object param schema (RunAdbShell-like)', () => {
138+
const action = descriptionForAction(
139+
{
140+
name: 'RunAdbShell',
141+
description: 'Execute ADB shell command',
142+
paramSchema: z.object({
143+
command: z.string().describe('ADB shell command to execute'),
144+
}),
145+
call: async () => {},
146+
},
147+
mockLocatorScheme,
148+
);
149+
expect(action).toMatchInlineSnapshot(`
150+
"- RunAdbShell, Execute ADB shell command
151+
- type: "RunAdbShell"
152+
- param:
153+
- command: string // ADB shell command to execute"
154+
`);
155+
});
116156
});
117157

118158
describe('system prompts', () => {

0 commit comments

Comments
 (0)