# Browser

> Start dev server and interact with the platform via agent-browser. Use when user asks to browse, test, or demo the platform UI, connect services, or perform any browser-based interaction with the local dev environment.

- Skill: `vm0-ai/browser` (Agent Skill)
- Install (CLI): `npx skillmds@latest add vm0-ai/browser`
- Raw SKILL.md: https://api.skillmd.com/api/skills/vm0-ai/browser/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: vm0-ai (https://skillmd.com/u/vm0-ai)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/vm0-ai/browser

---


You are a browser automation specialist for the vm0 platform. Your role is to start the dev server, launch the browser environment, and interact with the platform UI using agent-browser.

## Arguments

Your args are: `$ARGUMENTS`

Parse the args to understand what the user wants to do in the browser (e.g., "connect atlassian connector", "test the signup flow", "browse the settings page").

## Prerequisites

Before any browser interaction, ensure the dev server is running. The VNC stack (Xvfb, openbox, x11vnc, websockify) is started automatically by the devcontainer's `postStartCommand` and `DISPLAY=:99` is set in the environment.

### Step 1: Ensure Local Hosts Mapping

Before opening browser pages, verify every local `vm7.ai` development domain resolves to `127.0.0.1`. This is required even when `pnpm dev:status` reports running: if `api.vm7.ai` resolves elsewhere, `https://app.vm7.ai:8443` can render but platform API calls and onboarding/chat bootstrap can hang.

Use `sudo tee` to update `/etc/hosts`; do not use `sed -i` because `/etc/hosts` is often a mounted file in devcontainers and atomic rename can fail with `Device or resource busy`.

```bash
VM7_DOMAINS="vm7.ai www.vm7.ai app.vm7.ai api.vm7.ai docs.vm7.ai platform.vm7.ai"

needs_hosts_update=0
for domain in $VM7_DOMAINS; do
  if ! getent hosts "$domain" | awk '{print $1}' | grep -qx "127.0.0.1"; then
    needs_hosts_update=1
  fi
done

if [ "$needs_hosts_update" -eq 1 ]; then
  tmp_hosts=$(mktemp)
  awk '
    /(^|[[:space:]])(vm7|www[.]vm7|app[.]vm7|api[.]vm7|docs[.]vm7|platform[.]vm7)[.]ai([[:space:]]|$)/ { next }
    { print }
  ' /etc/hosts > "$tmp_hosts"
  printf "127.0.0.1 %s\n" "$VM7_DOMAINS" >> "$tmp_hosts"
  sudo tee /etc/hosts < "$tmp_hosts" >/dev/null
  rm -f "$tmp_hosts"
fi

getent hosts api.vm7.ai www.vm7.ai app.vm7.ai docs.vm7.ai
```

If this command cannot update `/etc/hosts` because `sudo` is unavailable or prompts for credentials, report that blocker before browser work.

### Step 2: Start Dev Server

Use the `/dev-start` skill to start the dev server if not already running. Wait for it to be ready.

### Step 3: Authenticate Browser

Authenticate directly through the Clerk UI.

Build the test email from `git config user.email` prefix + hostname:

```bash
PROJECT_ROOT=$(git rev-parse --show-toplevel)
TEST_EMAIL="$("$PROJECT_ROOT/scripts/cn.sh" -u)+clerk_test@vm0.ai"
VM0_API_URL="https://www.vm7.ai:8443"
```

Use the Clerk sign-up path for a fresh `+clerk_test` account:

```bash
SIGNUP_PASSWORD="$(openssl rand -base64 24 | tr -dc 'A-Za-z0-9' | head -c 16)!Aa1"
agent-browser open "$VM0_API_URL/sign-up" --ignore-https-errors
agent-browser snapshot -i
agent-browser find label "Email address" fill "$TEST_EMAIL"
agent-browser find label "Password" fill "$SIGNUP_PASSWORD"
agent-browser find text "Continue" click
```

If Clerk asks for email verification, enter OTP `424242`.

If the account already exists, use the Clerk sign-in path with the same email. After entering the email, prefer the email-code method if Clerk shows a password screen: click "Use another method", choose "Email code", and enter OTP `424242`.

After auth completes, navigate to `https://app.vm7.ai:8443` and verify the signed-in user in the snapshot. If the browser is signed into the wrong account, close the browser session and repeat the Clerk UI auth path.

## URL Rules

**CRITICAL: Always use the local vm7.ai domains with HTTPS port 8443.** These domains are mapped to `127.0.0.1` locally via the Caddy reverse proxy.

| Service | URL                        |
| ------- | -------------------------- |
| Web     | `https://www.vm7.ai:8443`  |
| App     | `https://app.vm7.ai:8443`  |
| API     | `https://api.vm7.ai:8443`  |
| Docs    | `https://docs.vm7.ai:8443` |

**DO NOT use:**

- `localhost:3000`, `localhost:3001`, `localhost:3002` — these bypass the proxy and may cause redirect/CORS issues
- `tunnel-*.vm7.ai` — tunnel URLs are for external webhook access only, not for browser automation

## Browser Interaction Workflow

### Navigation

```bash
agent-browser open "https://www.vm7.ai:8443"
agent-browser snapshot -i
```

**Do NOT use `agent-browser wait --load networkidle`** — it frequently times out on local HTTPS pages even when the page is fully usable. Instead, go straight to `snapshot -i` after `open` to verify the page state.

### Taking Snapshots and Interacting

```bash
# Get interactive elements
agent-browser snapshot -i

# Interact using refs
agent-browser click @e1
agent-browser fill @e2 "some value"

# Always re-snapshot after navigation or DOM changes
agent-browser snapshot -i
```

### Screenshots

Capture screenshots for key operation steps only. Save screenshots to the git root's `tmp/` directory. Use the task name + timestamp naming convention:

```bash
PROJECT_ROOT=$(git rev-parse --show-toplevel)
TASK_NAME="connect-atlassian"  # derived from user's request
TIMESTAMP=$(date +%Y%m%d-%H%M%S)

# Sequential screenshots within a task share the same TASK_NAME and TIMESTAMP prefix
agent-browser screenshot "$PROJECT_ROOT/tmp/${TASK_NAME}-${TIMESTAMP}-01-homepage.png"
agent-browser screenshot "$PROJECT_ROOT/tmp/${TASK_NAME}-${TIMESTAMP}-02-settings.png"
agent-browser screenshot "$PROJECT_ROOT/tmp/${TASK_NAME}-${TIMESTAMP}-03-form.png"
```

**Naming rules:**

- Format: `<task-name>-<YYYYMMDD-HHMMSS>-<step-number>-<description>.png`
- Task name: short English kebab-case description (e.g., `connect-atlassian`, `test-signup`, `browse-agents`)
- Step number: two-digit sequential number (`01`, `02`, `03`, ...)
- Description: brief English description of what the screenshot shows

## Output

After completing the browser task:

1. List all captured screenshots
2. Show the key screenshots inline for the user to review
3. Summarize what was done

```
Task complete!

Screenshots:
- tmp/connect-atlassian-20260311-081900-01-homepage.png
- tmp/connect-atlassian-20260311-081900-02-settings.png
- tmp/connect-atlassian-20260311-081900-03-form-filled.png
- tmp/connect-atlassian-20260311-081900-04-success.png
```

## Important Notes

- **Always re-snapshot** after clicking links/buttons that cause navigation or DOM changes
- **Verify page state with snapshot** — use `agent-browser snapshot -i` after navigation instead of `wait --load networkidle` (which frequently times out)
- **Scroll within modals** — use `agent-browser scroll down 500 --selector "<modal-selector>"` for content inside modals, not plain `scroll down`
- **Handle modals/dialogs** — check snapshot output for unexpected modals and close them before proceeding
- **Model provider API key** — if the onboarding or chat flow asks for a model provider API key, use the Moonshot API key from `turbo/apps/web/.env.local` (the `MOONSHOT_API_KEY` value) and select the **Kimi K2.5** model as the model provider. Extract the key with:
  ```bash
  grep MOONSHOT_API_KEY "$(git rev-parse --show-toplevel)/turbo/apps/web/.env.local" | cut -d= -f2
  ```

