ワンクリックで
mock-websites
How to set up and manipulate mock web application state via HTTP APIs for CUA-Gym tasks. For setup-gen and reward-gen agents.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
How to set up and manipulate mock web application state via HTTP APIs for CUA-Gym tasks. For setup-gen and reward-gen agents.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
How to programmatically create, modify, and verify Blender 3D scenes using Python bpy (headless background mode, .blend files, render output). For setup-gen and reward-gen agents.
How to programmatically set up, control, and verify Google Chrome browser state using Python (Playwright CDP, SQLite, JSON configs). For setup-gen and reward-gen agents.
How to programmatically set up, manipulate, and verify draw.io diagram state using Python (Playwright CDP, XML injection via Edit Diagram menu, DOM verification). For setup-gen and reward-gen agents.
How to programmatically set up, manipulate, and verify Excalidraw whiteboard state using Python (Playwright CDP, localStorage JSON, scene elements). For setup-gen and reward-gen agents.
How to programmatically create, modify, and verify images using Python Pillow, NumPy, OpenCV, and scikit-image. For setup-gen and reward-gen agents.
How to set up and manipulate Grafana dashboard, alerting, and admin state via HTTP APIs for CUA-Gym tasks. For setup-gen and reward-gen agents.
| name | mock_websites |
| description | How to set up and manipulate mock web application state via HTTP APIs for CUA-Gym tasks. For setup-gen and reward-gen agents. |
| user-invocable | false |
This skill teaches setup-gen how to create initial web app state and golden patches for mock website tasks. Unlike file-based domains, mock website tasks involve HTTP state injection — there are no local files on the VM.
requests, json, uuidhttps://cua-gym-<name>.xlang.aiAll mocks are publicly deployed. The URL pattern is https://cua-gym-<name>.xlang.ai where <name> is the directory name without _mock, with underscores replaced by hyphens.
| Mock | Public URL | SCHEMA.md |
|---|---|---|
| asana_mock | https://cua-gym-asana.xlang.ai | openrlvr-mock/asana_mock/SCHEMA.md |
| aws_console_mock | https://cua-gym-aws-console.xlang.ai | openrlvr-mock/aws_console_mock/SCHEMA.md |
| discord_mock | https://cua-gym-discord.xlang.ai | openrlvr-mock/discord_mock/SCHEMA.md |
| docusign_mock | https://cua-gym-docusign.xlang.ai | openrlvr-mock/docusign_mock/SCHEMA.md |
| github_mock | https://cua-gym-github.xlang.ai | openrlvr-mock/github_mock/SCHEMA.md |
| gitlab_mock | https://cua-gym-gitlab.xlang.ai | openrlvr-mock/gitlab_mock/SCHEMA.md |
| gmail_mock | https://cua-gym-gmail.xlang.ai | openrlvr-mock/gmail_mock/SCHEMA.md |
| jira_mock | https://cua-gym-jira.xlang.ai | openrlvr-mock/jira_mock/SCHEMA.md |
| linkedin_mock | https://cua-gym-linkedin.xlang.ai | openrlvr-mock/linkedin_mock/SCHEMA.md |
| notion_mock | https://cua-gym-notion.xlang.ai | openrlvr-mock/notion_mock/SCHEMA.md |
| reddit_mock | https://cua-gym-reddit.xlang.ai | openrlvr-mock/reddit_mock/SCHEMA.md |
| salesforce_mock | https://cua-gym-salesforce.xlang.ai | openrlvr-mock/salesforce_mock/SCHEMA.md |
| slack_mock | https://cua-gym-slack.xlang.ai | openrlvr-mock/slack_mock/SCHEMA.md |
| trello_mock | https://cua-gym-trello.xlang.ai | openrlvr-mock/trello_mock/SCHEMA.md |
| twitter_mock | https://cua-gym-twitter.xlang.ai | openrlvr-mock/twitter_mock/SCHEMA.md |
| youtube_mock | https://cua-gym-youtube.xlang.ai | openrlvr-mock/youtube_mock/SCHEMA.md |
Every mock exposes identical HTTP endpoints:
/post?sid=<sid> — State Injection| Action | Body | Effect |
|---|---|---|
set | {"action":"set", "state":{...}} | Writes current_state AND creates initial_state (if first write). Used by initial_setup.py. |
set_current | {"action":"set_current", "state":{...}} | Writes ONLY current_state. Never touches initial_state. Used by golden_patch.py. |
reset | {"action":"reset"} | Deletes both current and initial state files. |
All actions support "merge": true to deep-merge into existing state instead of replacing.
/go?sid=<sid> — State InspectionReturns:
{
"initial_state": { ... },
"current_state": { ... },
"state_diff": { ... }
}
initial_state = snapshot from first action:"set" callcurrent_state = latest state (updated by UI interactions or set_current)state_diff = keys that changed between initial and current/state?sid=<sid> — Raw State ReadReturns {stored_state, has_custom_state, sid}.
/upload?sid=<sid> — File UploadUpload files (attachments, images, documents) to the mock server. Files are stored per-session and served via /files/.
Request: multipart/form-data with one or more file fields.
Response:
{
"success": true,
"files": [
{
"original_name": "report.pdf",
"stored_name": "a1b2c3d4_report.pdf",
"size": 12345,
"content_type": "application/pdf",
"url": "/files/<sid>/a1b2c3d4_report.pdf"
}
]
}
Usage in initial_setup.py — upload a file and reference its URL in state:
import requests
# Upload a file
with open('/path/to/attachment.pdf', 'rb') as f:
resp = requests.post(
f'{BASE_URL}/upload?sid={sid}',
files={'file': ('attachment.pdf', f, 'application/pdf')},
timeout=30
)
uploaded = resp.json()['files'][0]
file_url = uploaded['url'] # e.g., /files/<sid>/a1b2c3d4_attachment.pdf
# Reference it in state injection
state['messages']['general'].append({
'messageId': 'msg_1',
'content': 'Here is the report',
'attachments': [{'name': 'attachment.pdf', 'url': file_url, 'size': uploaded['size']}],
# ... other fields
})
/files/<sid>/<filename> — Serve Uploaded FilesReturns the uploaded file with appropriate Content-Type header. Files are served with Content-Disposition: attachment.
The sid links initial_setup.py, golden_patch.py, and reward.py to the same state.
Flow:
initial_setup.py generates a UUID sid → writes to /tmp/task_web_sid on the VMinitial_setup.py POSTs action:"set" with the sid → creates both initial and current stategolden_patch.py reads sid from /tmp/task_web_sid → POSTs action:"set_current" → updates ONLY current statereward.py reads sid from /tmp/task_web_sid → GETs /go?sid=<sid> → compares initial vs current# Generate and persist sid (initial_setup.py)
import uuid
sid = str(uuid.uuid4())
with open('/tmp/task_web_sid', 'w') as f:
f.write(sid)
# Read sid (golden_patch.py and reward.py)
with open('/tmp/task_web_sid') as f:
sid = f.read().strip()
Each mock has a schema file documenting the full state shape, default IDs, and observable state changes. Schemas are stored locally in this skill directory.
MANDATORY: Always read the schema before writing state injection code. The schema tells you:
currentUser, channels, messages for slack)To load the schema for a mock (do this in Step 0, immediately after reading this SKILL.md):
Read: .claude/skills/mock_websites/schemas/<mock_name>.md
Example — for a Slack task:
Read: .claude/skills/mock_websites/schemas/slack_mock.md
For multi-mock tasks, read ALL schemas for every mock listed in task_config.json's domains array.
Available schemas: asana_mock, aws_console_mock, discord_mock, docusign_mock, github_mock, gitlab_mock, gmail_mock, jira_mock, linkedin_mock, notion_mock, reddit_mock, salesforce_mock, slack_mock, trello_mock, twitter_mock, youtube_mock.
If no schema file exists for a mock, inspect the mock's default state via GET /go?sid=nonexistent to discover the state shape (it returns default data when no custom state is set).
"""
Initial Setup: <task_description>
Task ID: <task_id>
Domain: mock_websites
Mock: <mock_name>
"""
import json
import os
import shlex
import subprocess
import time
import uuid
import requests
# --- Config ---
BASE_URL = 'https://cua-gym-<name>.xlang.ai' # e.g., cua-gym-slack.xlang.ai
sid = str(uuid.uuid4())
# Persist sid for golden_patch.py and reward.py
with open('/tmp/task_web_sid', 'w') as f:
f.write(sid)
# --- Build initial state ---
# Consult SCHEMA.md for the full state shape.
# Include ALL required top-level keys. Missing keys → blank UI or crash.
state = {
# ... full state matching SCHEMA.md ...
}
# --- Inject state ---
resp = requests.post(
f'{BASE_URL}/post?sid={sid}',
json={'action': 'set', 'state': state},
timeout=30
)
assert resp.status_code == 200, f'State injection failed: {resp.text}'
print(f'State injected: sid={sid}')
# --- Verify ---
go = requests.get(f'{BASE_URL}/go?sid={sid}', timeout=10).json()
assert go['initial_state'] is not None, 'initial_state is None after injection'
print('Verified: initial_state and current_state are set')
# --- Launch browser ---
def launch_gui(command, delay_sec=1.0):
env = os.environ.copy()
env['DISPLAY'] = ':0'
subprocess.Popen(
shlex.split(command),
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
env=env,
)
time.sleep(delay_sec)
launch_gui(f'google-chrome "{BASE_URL}/?sid={sid}"', delay_sec=2.0)
print(f'GUI_READY: launched browser at {BASE_URL}/?sid={sid}')
CRITICAL: Always use action:"set_current". NEVER use action:"set" in golden_patch.py.
Using action:"set" would overwrite initial_state, making state_diff empty and breaking reward evaluation entirely.
"""
Golden Patch: <task_description>
Task ID: <task_id>
Domain: mock_websites
Mock: <mock_name>
Changes: <brief list of what this patch does>
"""
import copy
import json
import requests
# --- Config ---
BASE_URL = 'https://cua-gym-<name>.xlang.ai'
# Read sid from initial_setup.py
with open('/tmp/task_web_sid') as f:
sid = f.read().strip()
# --- Fetch current initial state ---
go = requests.get(f'{BASE_URL}/go?sid={sid}', timeout=10).json()
state = copy.deepcopy(go['initial_state'])
# --- Apply ONLY the minimal changes the task requires ---
# Example: send a message in #general
# state['messages']['general'].append({
# 'messageId': 'msg_new_1',
# 'senderId': 'user_1',
# 'content': 'Hello world!',
# 'timestamp': '2024-06-15T14:30:00Z',
# 'threadId': None,
# 'reactions': [],
# 'attachments': [],
# 'isEdited': False
# })
# --- Write ONLY current_state (preserve initial_state) ---
resp = requests.post(
f'{BASE_URL}/post?sid={sid}',
json={'action': 'set_current', 'state': state},
timeout=30
)
assert resp.status_code == 200, f'set_current failed: {resp.text}'
print(f'Golden state applied via set_current: sid={sid}')
# --- Verify diff exists ---
go2 = requests.get(f'{BASE_URL}/go?sid={sid}', timeout=10).json()
assert go2['state_diff'], 'state_diff is empty — golden state matches initial (no changes applied)'
print(f'Verified: state_diff is non-empty')
When task_config.json contains a domains list with multiple mocks:
{
"domains": ["slack_mock", "notion_mock"],
"task_instruction": "Copy the meeting notes from Slack #general to a new Notion page"
}
Use the same sid across all mocks:
# initial_setup.py — inject into ALL listed mocks
sid = str(uuid.uuid4())
with open('/tmp/task_web_sid', 'w') as f:
f.write(sid)
mocks = {
'slack_mock': 'https://cua-gym-slack.xlang.ai',
'notion_mock': 'https://cua-gym-notion.xlang.ai',
}
for name, url in mocks.items():
state = build_state_for(name) # mock-specific state
resp = requests.post(f'{url}/post?sid={sid}', json={'action': 'set', 'state': state}, timeout=30)
assert resp.status_code == 200
# Launch browser with primary mock
launch_gui(f'google-chrome "{mocks["slack_mock"]}/?sid={sid}"', delay_sec=2.0)
Golden patch similarly uses set_current on each mock that should change.
Instead of ls /home/user/, use curl to verify state:
python3 scripts/env_cli.py -c "<workdir>/env_config_initial.json" execute \
"sid=\$(cat /tmp/task_web_sid); curl -s 'https://cua-gym-slack.xlang.ai/go?sid='\$sid | python3 -m json.tool | head -60"
action:"set" in golden_patch.py is the #1 bug. It overwrites initial_state, making state_diff empty. Reward scripts that compare initial vs current will always see 0 diff → reward = 0. ALWAYS use action:"set_current".
Missing state keys cause blank pages. If you inject {"channels": [...]} without currentUser, users, messages, etc., the UI renders empty or crashes. Always provide ALL required top-level keys from SCHEMA.md.
Array keys are replaced, not merged. deepMerge treats arrays as atomic values. If you POST {"messages": {"general": [msg1]}} with merge: true, it replaces the entire general array. To add a message, fetch current state first, append, then write back.
sid must be alphanumeric + hyphens + underscores. The server sanitizes sid with [^a-zA-Z0-9_-]. UUIDs work perfectly. Do not use special characters.
State is NOT persisted in the browser. The browser fetches state from the server on page load via ?sid=xxx. If you inject state after the browser loads, the user must refresh. Always inject state BEFORE launching the browser.
/go without sid returns default state. Always include ?sid=<sid> in all API calls. Missing sid returns the app's built-in default data, not your injected state.
Timestamps should be ISO 8601. Most mocks expect "2024-06-15T14:30:00Z" format. Using Unix timestamps or other formats may cause rendering issues.
IDs must be unique within their collection. When adding new messages, channels, etc., generate unique IDs (e.g., msg_new_1, ch_custom_1). Duplicate IDs cause silent data corruption.
golden_patch should copy initial_state and apply minimal changes. Don't build golden state from scratch — start from go['initial_state'], deep-copy it, and modify only what the task requires. This ensures the state_diff accurately reflects task completion.
HTTPS is required. All mocks are served over HTTPS at cua-gym-*.xlang.ai. HTTP will not work.