@minicor/mcp-server 3.1.5 → 3.2.0
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 +251 -61
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/laminar-client.d.ts +5 -0
- package/dist/laminar-client.d.ts.map +1 -1
- package/dist/laminar-client.js +6 -0
- package/dist/laminar-client.js.map +1 -1
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +2 -0
- package/dist/lib.js.map +1 -1
- package/dist/prompts/build-rpa.d.ts.map +1 -1
- package/dist/prompts/build-rpa.js +86 -272
- package/dist/prompts/build-rpa.js.map +1 -1
- package/dist/prompts/debug-execution.js +1 -1
- package/dist/seed-skills.d.ts +13 -0
- package/dist/seed-skills.d.ts.map +1 -0
- package/dist/seed-skills.js +82 -0
- package/dist/seed-skills.js.map +1 -0
- package/dist/skills-service-client.d.ts +54 -0
- package/dist/skills-service-client.d.ts.map +1 -0
- package/dist/skills-service-client.js +93 -0
- package/dist/skills-service-client.js.map +1 -0
- package/dist/skills.d.ts +34 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +152 -0
- package/dist/skills.js.map +1 -0
- package/dist/sync.d.ts +18 -2
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +404 -104
- package/dist/sync.js.map +1 -1
- package/dist/tools/core.d.ts.map +1 -1
- package/dist/tools/core.js +12 -0
- package/dist/tools/core.js.map +1 -1
- package/dist/tools/skills.d.ts +3 -0
- package/dist/tools/skills.d.ts.map +1 -0
- package/dist/tools/skills.js +429 -0
- package/dist/tools/skills.js.map +1 -0
- package/dist/tools/sync-tools.d.ts.map +1 -1
- package/dist/tools/sync-tools.js +53 -12
- package/dist/tools/sync-tools.js.map +1 -1
- package/dist/tools/vm.js +1 -1
- package/dist/tools/workflow-ops.js +1 -1
- package/dist/tools/workflow-ops.js.map +1 -1
- package/package.json +4 -2
- package/skills/general/cdp-browser-automation.md +97 -0
- package/skills/general/data-extraction-strategies.md +64 -0
- package/skills/general/data-hydration-patterns.md +167 -0
- package/skills/general/data-passing-between-steps.md +46 -0
- package/skills/general/desktop-uiautomation.md +80 -0
- package/skills/general/rpa-testing-workflow.md +145 -0
- package/skills/general/session-keepalive.md +101 -0
- package/skills/general/smart-launch-patterns.md +213 -0
- package/skills/general/state-verification.md +56 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: desktop-uiautomation
|
|
3
|
+
description: Desktop RPA patterns using uiautomation, pywinauto, and pyautogui. Framework selection by app type, element-based selectors over coordinates, wait-for-element patterns, retry strategies, popup dismissal. Use when building desktop automations or Windows app RPA.
|
|
4
|
+
category: general
|
|
5
|
+
tags: [desktop, uiautomation, pywinauto, pyautogui, windows, jab]
|
|
6
|
+
priority: 10
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Desktop UI Automation
|
|
10
|
+
|
|
11
|
+
## Framework Selection
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
| App Type | Framework |
|
|
15
|
+
| ---------------------------- | --------------------------------------- |
|
|
16
|
+
| .NET / WPF / WinForms | `uiautomation` (default) or `pywinauto` |
|
|
17
|
+
| Java / Swing | `jab` (Java Access Bridge) |
|
|
18
|
+
| Electron / web-based desktop | `pywinauto` or `pyautogui` |
|
|
19
|
+
| Legacy Win32 | `uiautomation` |
|
|
20
|
+
| Unknown | Start with `uiautomation` |
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
Desktop scripts use `/execute` and run **one at a time** per VM (the physical screen is shared).
|
|
24
|
+
|
|
25
|
+
## Wait for Elements (Never `time.sleep` for UI Waits)
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
for _ in range(20):
|
|
29
|
+
el = auto.WindowControl(Name="Patient Search")
|
|
30
|
+
if el.Exists(0.5): break
|
|
31
|
+
time.sleep(0.5)
|
|
32
|
+
else:
|
|
33
|
+
raise TimeoutError("Patient Search not found after 10s")
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Click by Element Selector (Not Coordinates)
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
# YES — element-based, resilient
|
|
40
|
+
auto.ButtonControl(Name="Find").Click()
|
|
41
|
+
|
|
42
|
+
# NO — coordinate-based, fragile
|
|
43
|
+
# pyautogui.click(517, 432)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Retry Flaky Actions
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
for attempt in range(3):
|
|
50
|
+
try:
|
|
51
|
+
auto.ButtonControl(Name="Find").Click()
|
|
52
|
+
time.sleep(1)
|
|
53
|
+
if auto.ListControl(Name="Results").Exists(1): break
|
|
54
|
+
except Exception:
|
|
55
|
+
if attempt == 2: raise
|
|
56
|
+
time.sleep(2)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Dismiss Popups (Check Before Major Actions)
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
popup = auto.WindowControl(searchDepth=1, RegexName=".*Error.*|.*Warning.*")
|
|
63
|
+
if popup.Exists(0.5):
|
|
64
|
+
close_btn = popup.ButtonControl(Name="Close")
|
|
65
|
+
ok_btn = popup.ButtonControl(Name="OK")
|
|
66
|
+
if close_btn.Exists(0.5):
|
|
67
|
+
close_btn.Click()
|
|
68
|
+
elif ok_btn.Exists(0.5):
|
|
69
|
+
ok_btn.Click()
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Output (Always Structured JSON)
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
print(json.dumps({"status": "success", "patient": name, "data": results}))
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Mixed Browser + Desktop
|
|
79
|
+
|
|
80
|
+
Some browser automations need the physical screen (OS alerts, file upload dialogs, native file picker, CAPTCHA). These **cannot use `/execute/browser`** — use `/execute` instead and treat as desktop automation.
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rpa-testing-workflow
|
|
3
|
+
description: Mandatory testing workflow for RPA steps. Prescriptive tool call sequences for per-step testing through the Minicor executor, polling execution status, taking screenshots during runs, and end-to-end validation. Use EVERY TIME you build or modify RPA workflows.
|
|
4
|
+
category: general
|
|
5
|
+
tags: [testing, workflow, execution, validation, mandatory]
|
|
6
|
+
priority: 100
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# RPA Testing Workflow
|
|
10
|
+
|
|
11
|
+
This skill is MANDATORY. Every RPA step must be tested through the Minicor executor, not just `vm_execute_script`. Follow these exact sequences.
|
|
12
|
+
|
|
13
|
+
## Why `vm_execute_script` Is Not Enough
|
|
14
|
+
|
|
15
|
+
`vm_execute_script` runs raw Python directly on the VM. It does NOT go through the Minicor workflow engine. This means:
|
|
16
|
+
|
|
17
|
+
- `{{config.username}}`, `{{config.password}}`, and all `{{config.*}}` variables are NOT resolved — they appear as literal text
|
|
18
|
+
- `data.input` interpolation does NOT work — the JS wrapper that injects input values is not executed
|
|
19
|
+
- `data.step_N.response` references to previous step outputs do NOT work
|
|
20
|
+
- The `lam.httpRequest` / `lam.rpa` dispatch wrapper is NOT executed
|
|
21
|
+
- Recording, execution tracking, and monitoring are NOT triggered
|
|
22
|
+
|
|
23
|
+
A script that passes via `vm_execute_script` can and WILL fail when run through `execute_workflow`. You MUST test through the real executor.
|
|
24
|
+
|
|
25
|
+
## Per-Step Testing (After Each `create_rpa_flow`)
|
|
26
|
+
|
|
27
|
+
After saving a step with `create_rpa_flow`, IMMEDIATELY test it through Minicor:
|
|
28
|
+
|
|
29
|
+
### Step 1: Run the step in isolation
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
execute_workflow_async(
|
|
33
|
+
workflowId: <workflow_id>,
|
|
34
|
+
input: { ... real test data ... },
|
|
35
|
+
configurationId: <config_store_id>,
|
|
36
|
+
start_from_step: <this_step_order>,
|
|
37
|
+
end_at_step: <this_step_order>
|
|
38
|
+
)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
This runs ONLY this step through the real Minicor executor with config variable resolution and the JS wrapper.
|
|
42
|
+
|
|
43
|
+
### Step 2: Poll execution status
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
Loop every 5-10 seconds:
|
|
47
|
+
get_execution_status(workflowId, executionId)
|
|
48
|
+
|
|
49
|
+
While status is "RUNNING" or "PENDING":
|
|
50
|
+
- Take vm_screenshot to watch progress on the VM
|
|
51
|
+
- Continue polling
|
|
52
|
+
|
|
53
|
+
When status is "COMPLETED":
|
|
54
|
+
- Take vm_screenshot to verify final state
|
|
55
|
+
- Call get_full_execution to see step output and any recordings
|
|
56
|
+
- Proceed to next step
|
|
57
|
+
|
|
58
|
+
When status is "FAILED" or "ERROR":
|
|
59
|
+
- Take vm_screenshot to see the error state
|
|
60
|
+
- Call diagnose_execution for failure analysis
|
|
61
|
+
- Call get_full_execution for detailed error output
|
|
62
|
+
- Go to failure recovery (below)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Step 3: Verify the result
|
|
66
|
+
|
|
67
|
+
After the step completes successfully:
|
|
68
|
+
- `vm_screenshot` to visually confirm the expected state
|
|
69
|
+
- Check the execution output via `get_full_execution` — verify the step produced the expected data
|
|
70
|
+
- If the step reads data, verify the output JSON is correct
|
|
71
|
+
|
|
72
|
+
### Step 4: Only then proceed to the next step
|
|
73
|
+
|
|
74
|
+
Do NOT start writing the next step until the current one passes through the executor.
|
|
75
|
+
|
|
76
|
+
## Failure Recovery
|
|
77
|
+
|
|
78
|
+
When a step fails through the Minicor executor:
|
|
79
|
+
|
|
80
|
+
1. `diagnose_execution(workflowId, executionId)` — get failure analysis with hints
|
|
81
|
+
2. `get_full_execution(workflowId, executionId)` — see stdout, stderr, exit code, recording
|
|
82
|
+
3. `vm_screenshot` — see what state the VM is in
|
|
83
|
+
4. Identify the cause:
|
|
84
|
+
- **Config variable not resolving?** Check the config store has the right properties
|
|
85
|
+
- **Data passing broken?** Check the JS wrapper interpolation — load `get_skill("data-passing-between-steps")`
|
|
86
|
+
- **Script error?** Fix the Python, but test the fix through the executor, NOT `vm_execute_script`
|
|
87
|
+
5. `update_flow(flowId, program: <fixed_code>)` — update the step code
|
|
88
|
+
6. Re-run `execute_workflow_async` with `start_from_step`/`end_at_step` again
|
|
89
|
+
7. Repeat until the step passes through the executor
|
|
90
|
+
|
|
91
|
+
CRITICAL: Do NOT use `vm_execute_script` to "verify" a fix. The fix must pass through `execute_workflow_async`.
|
|
92
|
+
|
|
93
|
+
## End-to-End Testing
|
|
94
|
+
|
|
95
|
+
After ALL steps pass individually through the executor:
|
|
96
|
+
|
|
97
|
+
### Full workflow test
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
execute_workflow_async(
|
|
101
|
+
workflowId: <workflow_id>,
|
|
102
|
+
input: { ... real production-like data ... },
|
|
103
|
+
configurationId: <config_store_id>
|
|
104
|
+
)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
No `start_from_step` / `end_at_step` — run the ENTIRE workflow.
|
|
108
|
+
|
|
109
|
+
### Monitor the full run
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
Loop every 5-10 seconds:
|
|
113
|
+
get_execution_status(workflowId, executionId)
|
|
114
|
+
vm_screenshot (every 2-3 polls to watch progress)
|
|
115
|
+
|
|
116
|
+
On completion:
|
|
117
|
+
get_full_execution to verify all steps produced correct output
|
|
118
|
+
vm_screenshot to verify final state
|
|
119
|
+
|
|
120
|
+
On failure:
|
|
121
|
+
diagnose_execution
|
|
122
|
+
Identify which step failed
|
|
123
|
+
Fix with update_flow
|
|
124
|
+
Re-run the FULL workflow (not just the failing step)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Completion criteria
|
|
128
|
+
|
|
129
|
+
The workflow is NOT done until:
|
|
130
|
+
- `execute_workflow_async` returns with all steps in "COMPLETED" status
|
|
131
|
+
- The output data is correct (verify via `get_full_execution`)
|
|
132
|
+
- The VM is in the expected final state (verify via `vm_screenshot`)
|
|
133
|
+
|
|
134
|
+
For production workflows, also:
|
|
135
|
+
- Run the workflow a second time to verify idempotency
|
|
136
|
+
- Verify state verification passes (expectedPreState/expectedPostState)
|
|
137
|
+
|
|
138
|
+
## Common Mistakes to Avoid
|
|
139
|
+
|
|
140
|
+
1. **Testing with `vm_execute_script` and declaring done** — this is the #1 mistake. Always use `execute_workflow_async`.
|
|
141
|
+
2. **Not using `configurationId`** — config variables only resolve when a config store is attached to the execution.
|
|
142
|
+
3. **Fixing a script and testing with `vm_execute_script`** — even fixes must be verified through the executor.
|
|
143
|
+
4. **Not polling `get_execution_status`** — the async execution runs in the background. You must poll to know when it finishes.
|
|
144
|
+
5. **Skipping per-step testing** — testing only end-to-end makes it hard to isolate failures. Test each step individually first.
|
|
145
|
+
6. **Not taking screenshots during execution** — screenshots while the workflow runs show you what's actually happening on the VM.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: session-keepalive
|
|
3
|
+
description: Patterns for keeping RPA sessions alive using scheduled agents, health checks, and auto-recovery. Detecting session timeouts, re-authentication sequences, monitoring agent setup. Use when hardening production workflows that depend on persistent app sessions.
|
|
4
|
+
category: general
|
|
5
|
+
tags: [session, keepalive, monitoring, scheduled, recovery, production]
|
|
6
|
+
priority: 8
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Session Keepalive
|
|
10
|
+
|
|
11
|
+
Production RPA workflows depend on an active, authenticated session. Sessions time out. This skill covers how to detect and recover from session loss automatically.
|
|
12
|
+
|
|
13
|
+
## The Problem
|
|
14
|
+
|
|
15
|
+
- Web apps log you out after inactivity (15-60 minutes typically)
|
|
16
|
+
- Desktop apps lock or disconnect after RDP session timeout
|
|
17
|
+
- Citrix sessions disconnect after idle periods
|
|
18
|
+
- Data workflows fail silently when the session is gone
|
|
19
|
+
|
|
20
|
+
## Solution: Scheduled Health Check Agent
|
|
21
|
+
|
|
22
|
+
### Step 1: Build a Smart Launch workflow
|
|
23
|
+
|
|
24
|
+
Load `get_skill("smart-launch-patterns")` and build a Smart Launch workflow first. This is the recovery mechanism.
|
|
25
|
+
|
|
26
|
+
### Step 2: Create a scheduled monitoring agent
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
create_agent(
|
|
30
|
+
name: "session-keepalive",
|
|
31
|
+
task: "Run the Smart Launch workflow to verify the session is active. If it fails, take a screenshot, diagnose, and report.",
|
|
32
|
+
mode: "scheduled",
|
|
33
|
+
cronSchedule: "*/5 * * * *",
|
|
34
|
+
workspaceId: <id>,
|
|
35
|
+
vmUrl: "<tunnel_url>",
|
|
36
|
+
watchWorkflowId: <smart_launch_workflow_id>
|
|
37
|
+
)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Step 3: Add failure monitoring
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
create_agent(
|
|
44
|
+
name: "session-monitor",
|
|
45
|
+
task: "When the Smart Launch fails, take a screenshot, check what went wrong, re-run Smart Launch, and notify Slack.",
|
|
46
|
+
mode: "monitor",
|
|
47
|
+
watchWorkflowId: <smart_launch_workflow_id>,
|
|
48
|
+
recoveryTask: "Take a screenshot. If login page is showing, the session expired — re-run Smart Launch. If the app crashed, restart it. Report results to Slack.",
|
|
49
|
+
slackWebhookUrl: "<webhook>",
|
|
50
|
+
workspaceId: <id>,
|
|
51
|
+
vmUrl: "<tunnel_url>"
|
|
52
|
+
)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Detecting Session Loss
|
|
56
|
+
|
|
57
|
+
### Browser (CDP)
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
# Check if still on the app page vs redirected to login
|
|
61
|
+
current_url = cdp_eval(ws, 'window.location.href')
|
|
62
|
+
if '/login' in current_url or '/lock' in current_url:
|
|
63
|
+
print("Session expired — need to re-authenticate")
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Desktop (uiautomation)
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
# Check if the expected signed-in element still exists
|
|
70
|
+
window = auto.WindowControl(searchDepth=1, Name='MyApp')
|
|
71
|
+
if not window.Exists(3):
|
|
72
|
+
print("App window gone — need to relaunch")
|
|
73
|
+
else:
|
|
74
|
+
signed_in_indicator = window.HyperlinkControl(Name='Dashboard')
|
|
75
|
+
if not signed_in_indicator.Exists(3):
|
|
76
|
+
print("Signed out — need to re-authenticate")
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Recovery Sequence
|
|
80
|
+
|
|
81
|
+
1. Take a screenshot to document the failure state
|
|
82
|
+
2. Run the Smart Launch workflow
|
|
83
|
+
3. Verify the session is restored (check for the signed-in indicator)
|
|
84
|
+
4. If still failing, try a full restart (close app, relaunch, login)
|
|
85
|
+
5. Report results via Slack webhook or issue update
|
|
86
|
+
|
|
87
|
+
## RDP/Citrix Session Management
|
|
88
|
+
|
|
89
|
+
For desktop automations that require an active RDP session:
|
|
90
|
+
- The VM's MDS handles this — it runs as a scheduled task in the interactive session
|
|
91
|
+
- If the RDP session disconnects, MDS continues running but UI automation may fail
|
|
92
|
+
- The health check detects this (window controls become unresponsive)
|
|
93
|
+
- Recovery: The VM service can trigger a reconnect via `start_mds_on_vm`
|
|
94
|
+
|
|
95
|
+
## Production Checklist
|
|
96
|
+
|
|
97
|
+
- [ ] Smart Launch workflow built and tested
|
|
98
|
+
- [ ] Scheduled keepalive agent created (5-minute interval)
|
|
99
|
+
- [ ] Monitor agent watching Smart Launch for failures
|
|
100
|
+
- [ ] Slack notifications configured for failure alerts
|
|
101
|
+
- [ ] Recovery task handles all known failure modes (timeout, crash, disconnect)
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: smart-launch-patterns
|
|
3
|
+
description: Patterns for building Smart Launch workflows that ensure an app is running and authenticated before data workflows execute. Browser launch via CDP, desktop app launch via uiautomation, login/unlock detection, credential injection, MFA handling, popup dismissal. Use when building session management or app launch automation.
|
|
4
|
+
category: general
|
|
5
|
+
tags: [smart-launch, session, login, authentication, browser, desktop, cdp, mfa]
|
|
6
|
+
priority: 9
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Smart Launch Patterns
|
|
10
|
+
|
|
11
|
+
A Smart Launch workflow ensures the target application is running and authenticated before any data workflow executes. Every production workspace needs one.
|
|
12
|
+
|
|
13
|
+
## When to Build a Smart Launch
|
|
14
|
+
|
|
15
|
+
- **Production**: Always. Data workflows depend on an active session.
|
|
16
|
+
- **POC**: Skip it. Test with a manually opened app.
|
|
17
|
+
|
|
18
|
+
## Browser Smart Launch (CDP)
|
|
19
|
+
|
|
20
|
+
Pattern from PracticeFusion (Hillside workspace):
|
|
21
|
+
|
|
22
|
+
### Step 1: Check if browser is running
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
import urllib.request, json
|
|
26
|
+
|
|
27
|
+
def get_cdp_ws(tab_filter='myapp'):
|
|
28
|
+
try:
|
|
29
|
+
resp = urllib.request.urlopen('http://127.0.0.1:9222/json')
|
|
30
|
+
tabs = json.loads(resp.read())
|
|
31
|
+
for tab in tabs:
|
|
32
|
+
if tab_filter in tab.get('url', ''):
|
|
33
|
+
return tab['webSocketDebuggerUrl']
|
|
34
|
+
return tabs[0]['webSocketDebuggerUrl'] if tabs else None
|
|
35
|
+
except:
|
|
36
|
+
return None
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Step 2: Launch browser if not running
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import subprocess, time
|
|
43
|
+
|
|
44
|
+
def launch_browser(url):
|
|
45
|
+
subprocess.Popen([
|
|
46
|
+
r'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe',
|
|
47
|
+
'--remote-debugging-port=9222',
|
|
48
|
+
'--remote-allow-origins=*',
|
|
49
|
+
'--start-maximized',
|
|
50
|
+
'--no-first-run',
|
|
51
|
+
url
|
|
52
|
+
])
|
|
53
|
+
time.sleep(8)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Step 3: Detect state (login vs locked vs logged-in)
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
current_url = cdp_eval(ws, 'window.location.href')
|
|
60
|
+
|
|
61
|
+
if '/lock' in current_url:
|
|
62
|
+
# Screen locked — unlock with password
|
|
63
|
+
unlock_screen(ws, password)
|
|
64
|
+
elif '/login' in current_url:
|
|
65
|
+
# On login page — enter credentials
|
|
66
|
+
login(ws, username, password)
|
|
67
|
+
elif '/app/' in current_url:
|
|
68
|
+
# Already logged in — ready
|
|
69
|
+
pass
|
|
70
|
+
else:
|
|
71
|
+
# Unknown state — navigate to login
|
|
72
|
+
cdp_navigate(ws, login_url)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Step 4: Inject credentials (React-safe)
|
|
76
|
+
|
|
77
|
+
Escape credentials before injecting into JS to handle special characters (`'`, `\`, etc.):
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
safe_user = username.replace('\\', '\\\\').replace("'", "\\'")
|
|
81
|
+
safe_pass = password.replace('\\', '\\\\').replace("'", "\\'")
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Then use the escaped values in the CDP eval:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
cdp_eval(ws, f"""
|
|
88
|
+
(function() {{
|
|
89
|
+
var emailInput = document.querySelector('input[type="email"]');
|
|
90
|
+
var pwInput = document.querySelector('input[type="password"]');
|
|
91
|
+
var setter = Object.getOwnPropertyDescriptor(
|
|
92
|
+
window.HTMLInputElement.prototype, "value"
|
|
93
|
+
).set;
|
|
94
|
+
if (emailInput) {{
|
|
95
|
+
setter.call(emailInput, '{username}');
|
|
96
|
+
emailInput.dispatchEvent(new Event('input', {{bubbles: true}}));
|
|
97
|
+
emailInput.dispatchEvent(new Event('change', {{bubbles: true}}));
|
|
98
|
+
}}
|
|
99
|
+
if (pwInput) {{
|
|
100
|
+
setter.call(pwInput, '{password}');
|
|
101
|
+
pwInput.dispatchEvent(new Event('input', {{bubbles: true}}));
|
|
102
|
+
pwInput.dispatchEvent(new Event('change', {{bubbles: true}}));
|
|
103
|
+
}}
|
|
104
|
+
var btns = document.querySelectorAll('button');
|
|
105
|
+
for (var i = 0; i < btns.length; i++) {{
|
|
106
|
+
if (btns[i].textContent.trim().toLowerCase().includes('log in')) {{
|
|
107
|
+
btns[i].click(); break;
|
|
108
|
+
}}
|
|
109
|
+
}}
|
|
110
|
+
}})();
|
|
111
|
+
""")
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Step 5: Handle MFA
|
|
115
|
+
|
|
116
|
+
After login, check if MFA is required:
|
|
117
|
+
- If URL contains 'securitycheck' or 'securitycode', MFA is needed
|
|
118
|
+
- Use `tfa_generate_totp` or `tfa_request_sms_otp` to get the code
|
|
119
|
+
- Inject the code via CDP
|
|
120
|
+
|
|
121
|
+
## Desktop Smart Launch (uiautomation)
|
|
122
|
+
|
|
123
|
+
Pattern from athenaPractice (Novelle workspace):
|
|
124
|
+
|
|
125
|
+
### Step 1: Check if window exists
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
import uiautomation as auto
|
|
129
|
+
|
|
130
|
+
win = auto.WindowControl(searchDepth=1, Name='athenaPractice')
|
|
131
|
+
window_exists = win.Exists(3)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Step 2: Conditional launch
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
if not window_exists:
|
|
138
|
+
import subprocess, os
|
|
139
|
+
app_path = os.path.join(os.environ["PUBLIC"], "Desktop", "MyApp.lnk")
|
|
140
|
+
subprocess.Popen(["powershell", "-Command", f'Start-Process "{app_path}"'])
|
|
141
|
+
time.sleep(15)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Step 3: Check if signed in
|
|
145
|
+
|
|
146
|
+
Look for a known UI element that only appears when logged in:
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
window = auto.WindowControl(searchDepth=1, Name='athenaPractice')
|
|
150
|
+
window.SetActive()
|
|
151
|
+
|
|
152
|
+
# Look for an element that proves we're signed in
|
|
153
|
+
chart = window.HyperlinkControl(Name='Chart')
|
|
154
|
+
signed_in = chart.Exists(3)
|
|
155
|
+
|
|
156
|
+
if not signed_in:
|
|
157
|
+
# Also try other control types
|
|
158
|
+
chart = window.TextControl(Name='Chart')
|
|
159
|
+
signed_in = chart.Exists(1)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Step 4: Conditional login
|
|
163
|
+
|
|
164
|
+
Use data passing to skip steps that aren't needed:
|
|
165
|
+
|
|
166
|
+
```javascript
|
|
167
|
+
(data) => {
|
|
168
|
+
const windowExists = data.step_1?.response?.resultData?.windowExists ?? false;
|
|
169
|
+
if (windowExists) {
|
|
170
|
+
return {"skipped": true, "message": "Window already exists"};
|
|
171
|
+
}
|
|
172
|
+
// Otherwise return the launch script
|
|
173
|
+
return { "lam.rpa": { script: launchScript, channelId: "{{config.channelId}}" } };
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Step 5: Dismiss popups
|
|
178
|
+
|
|
179
|
+
After login, dismiss any startup dialogs:
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
import time
|
|
183
|
+
time.sleep(3)
|
|
184
|
+
popup = auto.WindowControl(searchDepth=1, RegexName=".*Update.*|.*Notice.*|.*Alert.*")
|
|
185
|
+
if popup.Exists(2):
|
|
186
|
+
ok_btn = popup.ButtonControl(Name="OK") or popup.ButtonControl(Name="Close")
|
|
187
|
+
if ok_btn.Exists(1):
|
|
188
|
+
ok_btn.Click()
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Workflow Structure
|
|
192
|
+
|
|
193
|
+
A typical Smart Launch workflow has 4-6 steps:
|
|
194
|
+
1. Check if app/browser is running
|
|
195
|
+
2. Launch if not running
|
|
196
|
+
3. Check if signed in
|
|
197
|
+
4. Login if not signed in
|
|
198
|
+
5. Handle MFA (if applicable)
|
|
199
|
+
6. Dismiss popups
|
|
200
|
+
|
|
201
|
+
Use conditional execution via the JS wrapper — steps can return `{"skipped": true}` when not needed.
|
|
202
|
+
|
|
203
|
+
## Config Store Setup
|
|
204
|
+
|
|
205
|
+
Smart Launch workflows reference credentials from config stores:
|
|
206
|
+
- `{{config.username}}` / `{{config.password}}` — app credentials
|
|
207
|
+
- `{{config.url}}` — login URL
|
|
208
|
+
- `{{config.channelId}}` — RPA channel ID for desktop dispatch
|
|
209
|
+
- `{{config.totp_secret_id}}` — for TOTP-based MFA
|
|
210
|
+
|
|
211
|
+
## Health Check Pattern
|
|
212
|
+
|
|
213
|
+
Create a simple workflow that runs the Smart Launch and reports status. Schedule it via `create_agent mode: "scheduled"` with a cron expression (e.g., every 5 minutes). This keeps the session alive and auto-recovers from timeouts.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: state-verification
|
|
3
|
+
description: LLM-based UI state verification for RPA steps using expectedPreState and expectedPostState. How to use generate_state_description and debug_rpa_step for regression detection. Use when adding verification to RPA steps or debugging flaky automations.
|
|
4
|
+
category: general
|
|
5
|
+
tags: [verification, state, debugging, regression, screenshots]
|
|
6
|
+
priority: 7
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# State Verification
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
Every RPA step can include `expectedPreState` and `expectedPostState` — natural language descriptions of what the screen should look like before and after the step runs. The MDS uses LLM-based screenshot analysis to verify these conditions, catching regressions when the target app's UI changes.
|
|
14
|
+
|
|
15
|
+
## Using `debug_rpa_step`
|
|
16
|
+
|
|
17
|
+
`debug_rpa_step` runs a script with full diagnostics:
|
|
18
|
+
|
|
19
|
+
- Takes a **before screenshot**
|
|
20
|
+
- Executes the script
|
|
21
|
+
- Takes an **after screenshot**
|
|
22
|
+
- Returns stdout, stderr, exit code, and both screenshots
|
|
23
|
+
- Optionally verifies against `expectedPreState` / `expectedPostState`
|
|
24
|
+
|
|
25
|
+
Use during development to validate each step before saving.
|
|
26
|
+
|
|
27
|
+
## Writing State Descriptions
|
|
28
|
+
|
|
29
|
+
Use `generate_state_description` to author state descriptions from the current screen. Descriptions should be:
|
|
30
|
+
|
|
31
|
+
- **Specific enough** to catch real regressions (e.g., "The Patient Search dialog is open with an empty MRN field")
|
|
32
|
+
- **General enough** to not break on minor UI changes (e.g., don't mention exact pixel positions or colors)
|
|
33
|
+
- **Focused on the relevant area** — describe what matters for this step, not the entire screen
|
|
34
|
+
|
|
35
|
+
## Workflow
|
|
36
|
+
|
|
37
|
+
1. Navigate to the pre-state → `generate_state_description` → save as `expectedPreState`
|
|
38
|
+
2. Run the automation step
|
|
39
|
+
3. Verify the result → `generate_state_description` → save as `expectedPostState`
|
|
40
|
+
4. Pass both to `create_rpa_flow` when saving the step
|
|
41
|
+
|
|
42
|
+
## When State Verification Catches Issues
|
|
43
|
+
|
|
44
|
+
- App updated its UI (new buttons, moved fields, changed labels)
|
|
45
|
+
- Login session expired (expected app screen, got login page)
|
|
46
|
+
- Popup or error dialog appeared unexpectedly
|
|
47
|
+
- Previous step failed silently, leaving the app in wrong state
|
|
48
|
+
|
|
49
|
+
## Debugging Tools
|
|
50
|
+
|
|
51
|
+
- `**debug_rpa_step`** — runs script with before/after screenshots + full diagnostics
|
|
52
|
+
- `**vm_reset_state`** — manage windows: focus an app, minimize all, close dialogs
|
|
53
|
+
- `**vm_read_clipboard**` — read clipboard after a copy operation
|
|
54
|
+
- `**vm_screenshot_region**` — crop/zoom a specific screen region
|
|
55
|
+
- `**batch_test_rpa**` — run the workflow with multiple test inputs
|
|
56
|
+
- **Video Recordings** — use `get_full_execution` to see recording references
|