@minicor/mcp-server 3.1.4 → 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 +2 -2
- package/dist/tools/vm.js.map +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,167 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: data-hydration-patterns
|
|
3
|
+
description: Patterns for building standard CRUD data workflows (Get Practitioners, Patients, Appointments, Encounter Notes). API-first vs RPA approaches, data output conventions, pagination, orchestration order, pass-through return steps. Use when building data extraction or synchronization workflows for healthcare/enterprise apps.
|
|
4
|
+
category: general
|
|
5
|
+
tags: [data, hydration, crud, practitioners, patients, appointments, notes, api, scraping]
|
|
6
|
+
priority: 8
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Data Hydration Patterns
|
|
10
|
+
|
|
11
|
+
Most Minicor workspaces follow a standard set of data workflows. This skill documents the patterns across production deployments.
|
|
12
|
+
|
|
13
|
+
## The Standard Workflow Set
|
|
14
|
+
|
|
15
|
+
Every EHR/EMR integration workspace typically needs:
|
|
16
|
+
|
|
17
|
+
1. **Get Practitioners** — list of doctors/providers
|
|
18
|
+
2. **Get Patients** — patient roster (all or by search)
|
|
19
|
+
3. **Get Patient By ID** — single patient detail
|
|
20
|
+
4. **Get Appointments** — schedule for a date range
|
|
21
|
+
5. **Get Encounter Notes** — clinical notes for a patient
|
|
22
|
+
6. **Create Encounter Note** — write back a note
|
|
23
|
+
7. **Verify Patient** — confirm a patient exists by name/DOB
|
|
24
|
+
|
|
25
|
+
## Two Approaches
|
|
26
|
+
|
|
27
|
+
### API-First (Preferred)
|
|
28
|
+
|
|
29
|
+
If the app has accessible internal APIs (discovered via CDP network interception or documentation):
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
import json, urllib.request
|
|
33
|
+
|
|
34
|
+
url = f'{base_url}/api/patients?page={page}&size=100'
|
|
35
|
+
req = urllib.request.Request(url, headers={'Authorization': f'Bearer {token}'})
|
|
36
|
+
resp = urllib.request.urlopen(req)
|
|
37
|
+
data = json.loads(resp.read())
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Pros**: Fast, reliable, handles pagination natively, returns structured data.
|
|
41
|
+
**Examples**: HSMC (Insta HMS API), Novelle SQL queries.
|
|
42
|
+
|
|
43
|
+
### RPA Scraping (Fallback)
|
|
44
|
+
|
|
45
|
+
When no API is available, scrape data from the UI via CDP or uiautomation:
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
# CDP: Execute JS to extract table data
|
|
49
|
+
table_data = cdp_eval(ws, """
|
|
50
|
+
(function() {
|
|
51
|
+
var rows = document.querySelectorAll('table tbody tr');
|
|
52
|
+
var data = [];
|
|
53
|
+
rows.forEach(function(row) {
|
|
54
|
+
var cells = row.querySelectorAll('td');
|
|
55
|
+
data.push({
|
|
56
|
+
name: cells[0]?.textContent?.trim(),
|
|
57
|
+
id: cells[1]?.textContent?.trim(),
|
|
58
|
+
dob: cells[2]?.textContent?.trim()
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
return JSON.stringify(data);
|
|
62
|
+
})();
|
|
63
|
+
""")
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Pros**: Works when no API exists.
|
|
67
|
+
**Cons**: Fragile, slow, pagination via scrolling.
|
|
68
|
+
**Examples**: Hillside (PracticeFusion CDP scraping).
|
|
69
|
+
|
|
70
|
+
## Data Output Convention
|
|
71
|
+
|
|
72
|
+
All data workflows should return structured JSON:
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"count": 42,
|
|
77
|
+
"data": [
|
|
78
|
+
{"id": "P001", "name": "John Doe", "dob": "1990-01-15"},
|
|
79
|
+
{"id": "P002", "name": "Jane Smith", "dob": "1985-07-22"}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
For single-record queries (Get Patient By ID):
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"patient_id": "P001",
|
|
89
|
+
"name": "John Doe",
|
|
90
|
+
"dob": "1990-01-15",
|
|
91
|
+
"phone": "555-1234"
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## The Pass-Through Return Step
|
|
96
|
+
|
|
97
|
+
The workflow engine returns whatever the LAST step produces. If your last step is RPA (cleanup/navigation), the data gets lost.
|
|
98
|
+
|
|
99
|
+
**Pattern**: Add a final GENERAL_FUNCTION step that passes through the data:
|
|
100
|
+
|
|
101
|
+
```javascript
|
|
102
|
+
(data) => {
|
|
103
|
+
return data.step_3?.data || data.step_3?.response;
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
This ensures the formatted data from step 3 becomes the workflow output.
|
|
108
|
+
|
|
109
|
+
## Pagination
|
|
110
|
+
|
|
111
|
+
### API Pagination
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
all_records = []
|
|
115
|
+
page = 1
|
|
116
|
+
while True:
|
|
117
|
+
data = fetch_page(page, page_size=100)
|
|
118
|
+
all_records.extend(data['records'])
|
|
119
|
+
if len(data['records']) < 100:
|
|
120
|
+
break
|
|
121
|
+
page += 1
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### CDP Scroll Pagination
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
previous_count = 0
|
|
128
|
+
while True:
|
|
129
|
+
# Scroll to bottom
|
|
130
|
+
cdp_eval(ws, 'window.scrollTo(0, document.body.scrollHeight)')
|
|
131
|
+
time.sleep(2)
|
|
132
|
+
|
|
133
|
+
# Count rows
|
|
134
|
+
count = int(cdp_eval(ws, 'document.querySelectorAll("table tbody tr").length'))
|
|
135
|
+
if count == previous_count:
|
|
136
|
+
break # No new rows loaded
|
|
137
|
+
previous_count = count
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Hydration Orchestration
|
|
141
|
+
|
|
142
|
+
When running multiple data workflows to populate a cache:
|
|
143
|
+
|
|
144
|
+
1. **Practitioners first** — no dependencies
|
|
145
|
+
2. **Appointments** — optionally filtered by practitioner IDs from step 1
|
|
146
|
+
3. **Patients from appointments** — derived from appointment data (step 2)
|
|
147
|
+
4. **Encounter notes** — for patients from step 3
|
|
148
|
+
|
|
149
|
+
This is the pattern used by HSMC (WFs 753-756) — each workflow is standalone but designed to chain.
|
|
150
|
+
|
|
151
|
+
## Workflow Step Structure
|
|
152
|
+
|
|
153
|
+
A typical data workflow has 3-4 steps:
|
|
154
|
+
|
|
155
|
+
1. **Navigate/Setup** (RPA) — navigate to the right page or prepare the API call
|
|
156
|
+
2. **Extract/Scrape** (RPA) — get the raw data via API or scraping
|
|
157
|
+
3. **Format** (GENERAL_FUNCTION) — transform raw data into the standard JSON format
|
|
158
|
+
4. **Return** (GENERAL_FUNCTION) — pass-through step that ensures data is the workflow output
|
|
159
|
+
|
|
160
|
+
## Config Store Properties
|
|
161
|
+
|
|
162
|
+
Standard properties for data workflows:
|
|
163
|
+
- `{{config.username}}` / `{{config.password}}` — credentials
|
|
164
|
+
- `{{config.url}}` or `{{config.base_url}}` — app URL
|
|
165
|
+
- `{{config.channelId}}` — RPA channel for desktop dispatch
|
|
166
|
+
- `{{config.sullyOrganizationId}}` — customer ID for middleware routing
|
|
167
|
+
- `{{config.BASE_API_URL}}` — middleware/API base URL
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: data-passing-between-steps
|
|
3
|
+
description: How to pass data between workflow steps in Minicor RPA workflows. JS-layer interpolation from data.input and data.step_N, config variables via {{config.xxx}}, common pitfalls with Python template vars. Use when building multi-step workflows that need to share data.
|
|
4
|
+
category: general
|
|
5
|
+
tags: [data-passing, workflow, steps, interpolation, config]
|
|
6
|
+
priority: 9
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Data Passing Between Steps
|
|
10
|
+
|
|
11
|
+
Workflow input is available as `data.input` in the **JS wrapper**, NOT in Python directly. Dynamic values must be interpolated in the JS layer, then embedded into the Python template literal.
|
|
12
|
+
|
|
13
|
+
## CORRECT — JS-Layer Interpolation
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
(data) => {
|
|
17
|
+
const mrn = data?.input?.mrn || "DEFAULT";
|
|
18
|
+
const pythonScript = `
|
|
19
|
+
mrn = "${mrn}"
|
|
20
|
+
# ... rest of Python script uses mrn variable
|
|
21
|
+
`;
|
|
22
|
+
return { "lam.httpRequest": { body: { script: pythonScript } } };
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## WRONG — Python Template Vars
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
mrn = "{{input.mrn}}" # WRONG — this is not a config variable
|
|
30
|
+
mrn = data.input.mrn # WRONG — data object doesn't exist in Python
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Config Variables
|
|
34
|
+
|
|
35
|
+
`{{config.username}}` and `{{config.password}}` ARE resolved by the workflow engine and work in both JS and Python layers. Use these for credentials and environment URLs via config stores. **NEVER** hardcode credentials.
|
|
36
|
+
|
|
37
|
+
## Previous Step Outputs
|
|
38
|
+
|
|
39
|
+
- `data.step_N.response` — for HTTP/RPA steps
|
|
40
|
+
- `data.step_N.data` — for GENERAL_FUNCTION steps
|
|
41
|
+
|
|
42
|
+
## Important Notes
|
|
43
|
+
|
|
44
|
+
- `create_rpa_flow` generates the JS wrapper automatically, but if the script needs dynamic input from `data.input` or `data.step_N`, you must construct the JS wrapper yourself or update the flow after creation to add JS-layer const declarations
|
|
45
|
+
- The **last step** is the workflow output. If you need structured data returned, the last step must output it. Don't put meaningful data in step N and cleanup in step N+1. Combine cleanup + data return in the last step, or add a GENERAL_FUNCTION step after RPA that formats the final result.
|
|
46
|
+
|
|
@@ -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)
|