Web Autopilot
by @kckyan
Record any web app operation once, AI turns it into a reusable automation tool. Use when: (1) automating repetitive tasks on any web application (reports, su...
clawhub install web-autopilotπ About This Skill
name: web-autopilot description: > Record any web app operation once, AI turns it into a reusable automation tool. Use when: (1) automating repetitive tasks on any web application (reports, submissions, data extraction), (2) creating no-code automation for any logged-in web app, (3) building callable tools from recorded browser sessions. Supports REST, GraphQL, form submissions, file uploads, any login method. Task types: query/export (data extraction) and submit (form submissions like expense reports, travel requests, payment requests).
Web Autopilot
> Record once in any web app, let AI handle it from now on.
Overview
Record β Analyze β Confirm Fields β Generate β Test β Register as Tool
π¬ Record User performs the workflow once in a real browser (after login)
π Analyze AI analyzes network traffic, classifies fixed/dynamic/session fields
β
Confirm Fields [Required for submit tasks] User confirms field classifications
π Generate Generates reusable TS script + field mapping
π§ͺ Test Iterative test loop, up to 5 rounds of auto-fix
π§ Register Register as an OpenClaw tool for direct invocation
Task Types
π Query / Export
Data extraction and report generation. Scripts run and output results automatically β no manual intervention needed. Examples: pull sales reports, extract project data, export revenue detailsπ Submit
Submit forms such as expense reports, travel requests, payment requests, etc. Each run requires dynamic parameters. Examples: submit travel request, submit expense report, submit payment requestThe key challenge for submit tasks: correctly distinguishing which fields are fixed vs. which change every time, and confirming with the user before generating the script.
Skill Directory
~/.openclaw/rpa/
βββ recordings//recording.json
βββ tasks//
β βββ task-meta.json
β βββ run.ts
β βββ field-mapping.json
βββ sessions/.session.json
Skill scripts: /opt/homebrew/lib/node_modules/openclaw/skills/web-autopilot/scripts/
Commands
1. record β Record a workflow
Ask user: task name, login URL or app URL.
cd /opt/homebrew/lib/node_modules/openclaw/skills/web-autopilotOption A: Start from login page (SSO, OAuth, username/password, etc.)
npx ts-node scripts/record.ts --name "my-task" --sso-url "https://login.example.com"Option B: Start directly from app (if already logged in or no login needed)
npx ts-node scripts/record.ts --name "my-task" --app-url "https://app.example.com"
Run in PTY mode (pty: true, background: true). User operates browser, types "done" when finished.
Note: --sso-url is a legacy parameter name; it works for any login URL (SSO, OAuth, plain login page, etc.).
2. analyze β Analyze the recording (AI does this)
Read recording.json, separate login traffic from business traffic, identify core APIs.
Key steps:
1. Read ~/.openclaw/rpa/recordings/ for overview
2. Parse recording.json to extract all API calls to app domain
3. For each POST/PUT/PATCH with meaningful body:
- Classify fields: FIXED / DYNAMIC / SESSION / RELATIONAL
- Detect protocol: rest-json / graphql / form-urlencoded / multipart
4. Map the complete API sequence (prerequisites β main operation β follow-ups)
5. Analyze ALL response fields and create field-mapping.json with human-readable labels
6. Create task-meta.json
7. [Submit tasks] After analysis, present the field classification confirmation table to the user (see below)
#### Field Classification
| Type | Meaning | Handling |
|------|---------|----------|
| FIXED | Same value every submission (approval flow ID, company entity, currency, expense type enumsβ¦) | Hardcoded in script |
| DYNAMIC | Different each submission (amount, date, reason, attachment pathβ¦) | Becomes CLI --parameter |
| SESSION | Auth tokens/cookies, auto-managed | Injected by session.ts |
| RELATIONAL | Requires a lookup from another API to get the ID (e.g., project ID, person IDβ¦) | Auto-queried in script, or exposed as DYNAMIC parameter |
#### Field Analysis Rules (MANDATORY)
Every field must have a human-readable label. Including system-generated field names.
Inference priority:
1. Data value type: timestamp (10^12-13) / monetary amount (contextual) / enum (fixed values) / URL / JSON object
2. Field name pattern: *time/*date/*_at β datetime | *amount/*price/*cost β monetary | *id/*_key β ID | *status/*state β status
3. Business context: infer from related fields, API endpoint names
4. If uncertain β annotate as (unknown meaning: sample value)
#### Field Confirmation Step (MANDATORY for Submit tasks)
After analysis, you must present the following confirmation table to the user and wait for confirmation before generating the script:
π Field Classification Confirmation β β
FIXED (hardcoded):
- approvalFlowId: "xxx" β Approval Flow ID
- companyId: "yyy" β Company Entity
- currency: "CNY" β Currency
π DYNAMIC (passed as parameters each run):
- amount β Amount (example: --amount 1500)
- startDate β Start Date (example: --startDate 2026-03-10)
- endDate β End Date (example: --endDate 2026-03-12)
- destination β Destination (example: --destination "New York")
- reason β Reason (example: --reason "Client visit")
- attachments β Attachment path (example: --attachments ~/Desktop/receipt.jpg)
π RELATIONAL (auto-queried):
- projectId β Project ID (auto-looked up by project name, --projectName "Project X")
β Needs confirmation (AI uncertain):
- field_abc123 β Unknown meaning (recorded value: "0"), suggest: FIXED("0") or DYNAMIC?
Please confirm the above classification or indicate any fields that need adjustment.
Only proceed to the Generate step after user confirmation.
#### CSV Export Rules (MANDATORY)
csv.writer + proper quoting to handle JSON fields containing commas3. generate β Generate the task script
Pre-generation checklist (Query/Export tasks):
Pre-generation checklist (Submit tasks):
--dry-run mode (prints request body without submitting, for testing)Submit task invocation example (written to task-meta.json usage field after generation):
# Preview (no actual submission)
npx ts-node run.ts --dry-run --amount 1500 --startDate 2026-03-10 ...Submit for real
npx ts-node run.ts --amount 1500 --startDate 2026-03-10 --destination "New York" --reason "Client visit"
4. test β Iterative test loop (max 5 rounds)
Run script β check output β if error: diagnose β fix β repeat.
| Error | Cause | Fix | |-------|-------|-----| | 401/403 | Session expired / wrong auth | Re-check auth headers, re-login | | 400 | Wrong field name/type | Compare with recording | | 404 | Wrong URL | Check URL exactly | | JSON parse error | Response is HTML | Log resp.raw |
5. run β Execute a registered task
npx ts-node ~/.openclaw/rpa/tasks//run.ts --param1 value1
6. list β List all tasks
npx ts-node /opt/homebrew/lib/node_modules/openclaw/skills/web-autopilot/scripts/run-task.ts --list
Session & Credential Management
Session (Cookie/Token Storage)
Sessions are cookie-based and work with any login method:
Session files: ~/.openclaw/rpa/sessions/
Credentials (Encrypted Storage)
Login credentials are stored encrypted (AES-256-GCM) in a separate file β never stored in plaintext.
File: ~/.openclaw/rpa/credentials.enc
RPA_CREDENTIAL_KEY env var# Manage credentials
npx ts-node scripts/utils/credentials.ts list # List saved domains + usernames
npx ts-node scripts/utils/credentials.ts save # Save manually
npx ts-node scripts/utils/credentials.ts delete # Delete
npx ts-node scripts/utils/credentials.ts extract # Extract from recording
Auto-Login Flow
When a session expires, the auto-login flow kicks in:
1. Read encrypted credentials for the target domain from credentials.enc
2. Select login strategy based on loginFlow.type
3. Launch browser (headless if credentials exist, headed if not)
4. Execute login steps β follow redirects β reach target app
5. Capture cookies/tokens β save new session
6. If all else fails β open headed browser for manual login (fallback)
#### Login Flow Types
When generating scripts, you must identify the login type from the recording and write it to the loginFlow field in task-meta.json:
| type | Scenario | Auto-login method | Example |
|------|----------|-------------------|---------|
| api | SSO/app provides a REST login endpoint, single POST completes auth | Call API directly β follow redirects | Enterprise SSO (POST /api/sso/login) |
| form | Single-page login form (username + password on same page) | Fill form fields β click submit | Common admin dashboards |
| multi-step | Multi-step login (email β next page β password β next page β possible 2FA) | Execute step sequence | Google, Microsoft, Okta |
| manual-only | Has CAPTCHA/2FA/risk control, cannot be fully automated | Open headed browser directly | Banking systems, strong CAPTCHA sites |
#### loginFlow Schema (task-meta.json)
{
"loginFlow": {
"type": "api", // api | form | multi-step | manual-only
"loginUrl": "https://sso.example.com",
"loginDomain": "sso.example.com",
"appDomain": "app.example.com", // ββ type=api specific fields ββ
"loginApiPath": "/api/sso/login",
"authType": "passwordAuth", // Optional, auth type field in API body
"appId": "1234567890", // Optional, SSO portal app ID (for forward redirect)
"appForwardUrl": "...", // Optional, direct redirect URL (alternative to appId)
// ββ type=form specific fields ββ
"usernameSelector": "input[name='email']", // Optional, custom selectors
"passwordSelector": "input[type='password']",
"submitSelector": "button[type='submit']",
// ββ type=multi-step specific fields ββ
"steps": [
{ "action": "fill", "selector": "input[type=email]", "field": "username" },
{ "action": "click", "selector": "#identifierNext" },
{ "action": "wait", "selector": "input[type=password]", "timeoutMs": 5000 },
{ "action": "fill", "selector": "input[type=password]", "field": "password" },
{ "action": "click", "selector": "#passwordNext" }
],
// ββ Common fields ββ
"successIndicator": "url_contains:app.example.com", // Condition to detect successful login
"postLoginWaitMs": 3000 // Wait time after login success (for cookies to settle)
}
}
#### Login Identification Guide for Analyze Step (MANDATORY)
During the analyze step, you must complete the following login analysis:
1. Extract credentials β credentials.ts extract (auto-detects username/password in POST body)
2. Identify login type β Inspect the login flow in the recording:
- Has a clear POST login/auth API β type = api
- Has form fill actions (password type input) on the same page β type = form
- Has multiple form fill actions with page navigations in between β type = multi-step
- Has CAPTCHA image requests or reCAPTCHA scripts β type = manual-only
3. Document the SSO β app redirect path:
- Does it use an appId forward?
- Does it use a redirect_uri callback?
- Where is the token β in URL query / response body / cookie?
4. Write loginFlow β Write all fields to task-meta.json
5. Sanitize β Replace passwords in recording.json with [REDACTED]
β οΈ If credentials.ts extract cannot extract credentials (e.g., Google multi-step login), prompt the user to save credentials manually:
npx ts-node scripts/utils/credentials.ts save accounts.google.com user@gmail.com 'password'
#### Login Code Templates for Script Generation
Choose the auto-login implementation based on loginFlow.type:
type=api (REST API login):
// API login β follow redirects β navigate to app
const resp = await page.evaluate(async (p) => {
const r = await fetch(p.url, { method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify(p.body), credentials: 'include' });
return { status: r.status, ok: r.ok };
}, { url: loginApiUrl, body: { authType, credential: { username, password } } });
type=form:
await page.fill(loginFlow.usernameSelector || 'input[name="username"]', cred.username);
await page.fill(loginFlow.passwordSelector || 'input[type="password"]', cred.password);
await page.click(loginFlow.submitSelector || 'button[type="submit"]');
type=multi-step:
for (const step of loginFlow.steps) {
if (step.action === 'fill') {
const value = step.field === 'username' ? cred.username : cred.password;
await page.fill(step.selector, value);
} else if (step.action === 'click') {
await page.click(step.selector);
} else if (step.action === 'wait') {
await page.waitForSelector(step.selector, { timeout: step.timeoutMs || 10000 });
}
}
type=manual-only:
// Open headed browser, wait for user to complete login manually
const browser = await pw.chromium.launch({ headless: false });
// ... wait for successIndicator
task-meta.json loginFlow example (SSO β enterprise app):
{
"loginFlow": {
"type": "api",
"loginUrl": "https://sso.example.com",
"loginDomain": "sso.example.com",
"loginApiPath": "/api/sso/login",
"authType": "passwordAuth",
"appId": "1234567890",
"appDomain": "app.example.com",
"successIndicator": "url_contains:app.example.com"
}
}
β οΈ Security Rules (MANDATORY)
1. Passwords in recording.json must be sanitized immediately after analysis (replace with [REDACTED])
2. credentials.enc is an encrypted binary file β do not attempt to read or edit directly
3. credentials.enc and sessions/ directory must never be committed to version control or shared
4. Skill packages (.skill) must not contain any credentials, sessions, or recording data
5. Generated task scripts (run.ts) must never hardcode any passwords
Known Issues & Lessons Learned
π Login flow must match app β don't assume one-size-fits-all
type=api mode (enterprise SSO β business app)type=multi-step + steps sequencetype=manual-onlyβ οΈ Node v25 ESM compatibility
require() is unavailabletsconfig.json in the task directory to force "module": "commonjs"require('/opt/.../node_modules/playwright')β οΈ Multi-tab traffic capture (fixed)
Some login flows or apps open new tabs. Recorder usescontext.on('request/response') to capture ALL tabs.π CSV must include ALL fields with human-readable labels
field_*, attr_*, custom_*) must be analyzed from sample dataπ Submit tasks: always confirm field classification before generating
--dry-run in generated scripts so users can verify the request body before committingFile Locations
| Item | Path |
|------|------|
| Recorder | scripts/record.ts |
| Task runner | scripts/run-task.ts |
| Session utility | scripts/utils/session.ts |
| Login helper | scripts/utils/login.ts |
| Recordings | ~/.openclaw/rpa/recordings/ |
| Generated tasks | ~/.openclaw/rpa/tasks/ |
| Sessions | ~/.openclaw/rpa/sessions/ |