Skip to content

Commit 8e31381

Browse files
authored
feat(computer): add rdp demo (#95)
1 parent 42b15e7 commit 8e31381

5 files changed

Lines changed: 183 additions & 0 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ test-results/
1616
.env
1717

1818
output/
19+
docs/
1920

2021
.DS_Store
2122

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ Here are some examples you can refer to:
1818
- [Vitest Demo](./android/vitest-demo/): Integrate Midscene with Android and Vitest.
1919
- [YAML Scripts Demo](./android/yaml-scripts-demo/): Automate Android with scripts in YAML. This is the easiest way to integrate Midscene with your existing Android project.
2020

21+
### Computer
22+
- [JavaScript SDK Demo](./computer/javascript-sdk-demo/): Integrate Midscene with your local desktop through `@midscene/computer`.
23+
- [Vitest Demo](./computer/vitest-demo/): Integrate Midscene with `@midscene/computer` and Vitest.
24+
- [Electron Demo](./computer/electron-demo/): Use Midscene with an Electron app.
25+
- [YAML Scripts Demo](./computer/yaml-scripts-demo/): Automate desktop tasks with scripts in YAML.
26+
- [RDP Demo](./computer/rdp-demo/): Control a remote Windows desktop directly over the RDP protocol.
27+
2128
## Connectivity Test
2229

2330
- [Connectivity Test](./connectivity-test/): Use this folder to test the connectivity of the LLM Service.

computer/rdp-demo/README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Computer RDP Demo
2+
3+
This demo shows how to use `@midscene/computer` to control a remote Windows desktop directly over the RDP protocol.
4+
5+
## Steps
6+
7+
### Preparation
8+
9+
Create `.env` file with the same Midscene model variables used by other examples:
10+
11+
```shell
12+
MIDSCENE_MODEL_BASE_URL="https://.../compatible-mode/v1"
13+
MIDSCENE_MODEL_API_KEY="sk-abcdefghijklmnopqrstuvwxyz"
14+
MIDSCENE_MODEL_NAME="qwen3-vl-plus"
15+
MIDSCENE_MODEL_FAMILY="qwen3-vl"
16+
```
17+
18+
Then open [demo.ts](./demo.ts) and update the placeholder values in the `rdpTarget` object:
19+
20+
```ts
21+
const rdpTarget = {
22+
host: 'REPLACE_WITH_YOUR_RDP_HOST',
23+
port: 3389,
24+
username: 'Admin',
25+
password: 'REPLACE_WITH_YOUR_RDP_PASSWORD',
26+
ignoreCertificate: true,
27+
adminSession: false,
28+
domain: undefined,
29+
securityProtocol: 'auto',
30+
};
31+
```
32+
33+
The demo validates the RDP target values before creating the agent. If you leave a placeholder unchanged, it will throw immediately.
34+
35+
If you want to use another model, refer to:
36+
https://midscenejs.com/model-common-config.html
37+
38+
### Run demo
39+
40+
```bash
41+
npm install
42+
43+
npm run test
44+
```
45+
46+
The demo will connect to the remote Windows desktop, wait for the remote framebuffer to become visible if the first frame is blank, open the Settings app, continue into the Windows Update page, read the visible page title and status summary back into structured JSON, and print the generated report path.
47+
48+
## Notes
49+
50+
- This demo uses protocol-level RDP control. Midscene sees the remote Windows framebuffer directly instead of controlling a local RDP client window.
51+
52+
## Reference
53+
54+
- https://midscenejs.com/computer-introduction.html
55+
- https://midscenejs.com/computer-api-reference.html

computer/rdp-demo/demo.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import {
2+
agentForRDPComputer,
3+
type RDPComputerAgentOpt,
4+
} from '@midscene/computer';
5+
import 'dotenv/config';
6+
7+
const rdpTarget = {
8+
host: 'REPLACE_WITH_YOUR_RDP_HOST',
9+
port: 3389,
10+
username: 'Admin',
11+
password: 'REPLACE_WITH_YOUR_RDP_PASSWORD',
12+
ignoreCertificate: true,
13+
adminSession: false,
14+
domain: undefined,
15+
securityProtocol: 'auto',
16+
} satisfies RDPComputerAgentOpt;
17+
18+
function validateDemoConfig() {
19+
for (const [key, value] of Object.entries({
20+
host: rdpTarget.host,
21+
password: rdpTarget.password,
22+
})) {
23+
const trimmed = value?.trim();
24+
if (!trimmed || trimmed.startsWith('REPLACE_WITH_')) {
25+
throw new Error(
26+
`Please update rdpTarget.${key} in computer/rdp-demo/demo.ts before running the demo.`,
27+
);
28+
}
29+
}
30+
}
31+
32+
async function waitForRemoteDesktopReady(
33+
agent: Awaited<ReturnType<typeof agentForRDPComputer>>,
34+
maxAttempts = 10,
35+
delayMs = 3_000,
36+
) {
37+
let lastError: unknown;
38+
39+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
40+
try {
41+
await agent.aiAssert(
42+
'The remote screenshot is not a blank white screen. Some visible Windows UI such as the taskbar, desktop icons, the Start menu, or an application window is present.',
43+
);
44+
return;
45+
} catch (error) {
46+
lastError = error;
47+
if (attempt === maxAttempts) {
48+
break;
49+
}
50+
await new Promise((resolve) => setTimeout(resolve, delayMs));
51+
}
52+
}
53+
54+
throw new Error(
55+
`The remote desktop never became visible after ${maxAttempts} attempts.`,
56+
{ cause: lastError },
57+
);
58+
}
59+
60+
async function main() {
61+
validateDemoConfig();
62+
63+
const agent = await agentForRDPComputer({
64+
...rdpTarget,
65+
aiActionContext:
66+
'You are controlling a remote Windows desktop directly through the RDP protocol. Every screenshot and action comes from the remote machine itself.',
67+
generateReport: true,
68+
});
69+
70+
try {
71+
await waitForRemoteDesktopReady(agent);
72+
73+
await agent.aiAct(
74+
'Click the Windows Start button, open the Settings app, then navigate to the Windows Update page. Stop only after the Windows Update page is clearly visible in the remote screenshot.',
75+
);
76+
77+
await agent.aiAssert(
78+
'The Windows Update page inside the Settings app is open and visible in the remote screenshot.',
79+
);
80+
81+
const windowsUpdateSummary = await agent.aiQuery<{
82+
pageTitle: string;
83+
statusSummary: string;
84+
}>(
85+
'{pageTitle: string, statusSummary: string}, read the visible Windows Update page and return its main page title and the short status summary shown near the top of the page.',
86+
);
87+
88+
console.log('Connected to remote desktop:', rdpTarget.host);
89+
console.log('Windows Update page title:', windowsUpdateSummary.pageTitle);
90+
console.log('Windows Update status:', windowsUpdateSummary.statusSummary);
91+
} finally {
92+
await agent.destroy();
93+
}
94+
95+
if (agent.reportFile) {
96+
console.log('Report saved to:', agent.reportFile);
97+
}
98+
}
99+
100+
main().catch((error) => {
101+
console.error(error);
102+
process.exitCode = 1;
103+
});

computer/rdp-demo/package.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "computer-rdp-demo",
3+
"private": true,
4+
"version": "1.0.0",
5+
"description": "Demo for controlling a remote Windows desktop over RDP with Midscene",
6+
"type": "module",
7+
"scripts": {
8+
"test": "tsx demo.ts"
9+
},
10+
"author": "",
11+
"license": "MIT",
12+
"devDependencies": {
13+
"@midscene/computer": "latest",
14+
"dotenv": "^16.4.5",
15+
"tsx": "4.20.1"
16+
}
17+
}

0 commit comments

Comments
 (0)