Install
openclaw skills install @tahcia/tahcia-consoleAutomate real Chrome browser on macOS via CLI, Chrome extension, and WebSocket; control mouse/keyboard for precise UI interactions without headless mode.
openclaw skills install @tahcia/tahcia-consoleTahcia controls your real Chrome via a Chrome extension + WebSocket session + Mac engine. NOT Playwright, NOT headless, NOT a separate browser instance.
https://api.tahcia.combrew tap tahcia/tahcia
brew install tahcia
Requires: Node.js
Installed to: /usr/local/Cellar/tahcia/<version>
CLI binary: /usr/local/bin/tahcia → tahcia.js
The tahcia-mac helper binary (macOS system control) is bundled with the brew formula.
LOOP: ANALYZE (screenshot + OCR + elements) → DECIDE ONE ACTION → EXECUTE → REVIEW result → DONE or repeat.
Every iteration: save your goal, current mini-goal, execution theory, and what you learn about using macOS. When you learn something (like coordinate systems, timing, app behavior), write it back to this SKILL.md so future-you benefits.
A loop iteration ends with either:
CLI (one-shot) → WebSocket → api.tahcia.com → Chrome Extension → Real Chrome
↓
tahcia-mac binary
(macOS mouse/keyboard)
wss://api.tahcia.com/wss?session=...&role=clienttahcia-mac for system-level inputtahcia "<sessionId:password>" "/command"
Each command = fresh connection. Wait for done:1 response, then wait ~500ms before next.
<sessionId>:<password> — e.g. 1b819a86b39c4a7e98596f0e4e72f640:b07c66b3ea
Stored in ~/.tahcia/config or TAHCIA_API_KEY env var. CLI does NOT use API key.
{ "api_key": "your_api_key_here" }
tahcia "key" "/mac.focus com.google.Chrome"
tahcia "key" "/mac.mouse.coord chrome"
sleep 1
tahcia "key" "/tab <N>"
sleep 2
tahcia "key" "/focus"
sleep 1
# then action
tahcia "key" "/mac.open com.company.SoftwareBundleId" or "/mac.focus com.company.SoftwareBundleId"
tahcia "key" "/mac.mouse.coord desktop"
sleep 5 # Remember each software may differ in load time. guess+5
# then action.
tahcia "key" "/console" — go to Console tab to wake extensionsleep 2tahcia "key" "/tab <N>" — switch backsleep 2tahcia "key" "/focus" — focus tab/elementstahcia "key" "/reload"
sleep 3-4 # wait for full page load
tahcia "key" "/elements"
Always cmd a → backspace first if the field might have existing text.
If you, openclaw, has access to any model apikey to perform vision analysis, you use /ss.last 2sec right after ss.screenshot, call frontier api directly from openclaw to analyze the image however you need.
For this setup, the Gemini API key is stored in ~/.gemini/api_key.txt. Use it with curl to call gemini-3.1-flash-lite for image analysis.
Step 1. Identify the core noun/subject the user is targeting (e.g., "whiskey glass"). Step 2. Scan the ACCESSIBILITY TREE and CURRENT SCREENSHOT for any visible elements, card listings, links, or buttons that partially or fully match this subject in their text content, labels, or tags. Step 3. If there is a direct or highly specific match (e.g., an item explicitly named "Whiskey Glass" when the user wants a whiskey glass, or a listing containing that keyword), your ONLY objective is to target it. Step 4. If the user uses pointing/action words (see, show, open, click, that, this, one) or is expressing frustration about an item on screen, DO NOT ask for clarification. You MUST generate a click command on the best matching element immediately. Step 5. Only leave commands empty and ask for clarification if there are absolutely ZERO matches or multiple identical matches that make action impossible. Step 6. If previous commands issued and previous screenshot provided, you must believe Tahcia already ran those. You shall never issue it wasn't properly performed. If clearly you are trying to type in a field that doesnt show up, it's better to end this with done:true, and tell user you are unable to perform task A and maybe user can check things out. Step 7. Remember, if you feel like an idiot, or human CEO in tech thinks you are an idiot, failing to complete asked tasks, it's better to give up and end this rather than burning tokens. ===END PRE-FLIGHT===
text saying you cannot find something when the item text is visible in your accessibility tree.text extremely short, professional, and future tense.text and leave commands empty.text by describing what you see and what actions will be taken.text short and direct. Minimize tokens.commands, otherwise commands must be filled.| Command | Description |
|---|---|
/tab <url> | Open new tab with URL |
/tab <N> | Switch to tab by console index |
/tab <tab_id> -ifnotcurrent <url> | Navigate existing tab (flag is script-only) |
/console | Go to Tahcia Console tab |
/focus | Focus current tab |
/reload | Reload current tab |
Uses the tahcia-mac binary. These generate REAL macOS input events (not JS events).
| Command | Description |
|---|---|
/mac.focus <bundle_id> | Focus app by bundle ID (e.g. com.google.Chrome) |
/mac.mouse.move <x> <y> <ms> | Move mouse to pixel (duration = smoothness) |
/mac.mouse.click <x> <y> <hold_ms> | Move + click at position (3rd param = time to let cursor arrive first) |
/mac.mouse.dblclick <x> <y> <hold_ms> | Double-click (needs 3rd param) |
/mac.mouse.drag <x> <y> <ex> <ey> <ms> | Real drag: mousedown → move → mouseup. Used for text selection |
/mac.key.type <text> | Type text via macOS events |
/mac.key.enter | Press Enter key |
/mac.key.shortcut <keys...> | Keyboard shortcut (e.g. cmd v, cmd a, cmd shift p, backspace) |
/mac.mouse.coord <desktop|chrome> | Set coordinate reference frame (default: chrome) |
/mac.screenshot | Capture macOS screen (returns Base64) |
/mac.ocr | OCR screen — all text blocks with pixel positions |
/mac.pixel <x> <y> | Get color of screen pixel |
/mac.windows | List all visible windows with positions/bounds |
/mac.open <bundle_id> | Launch app by bundle ID |
These dispatch JS events in the browser — they may NOT trigger real browser behavior
(like CAPTCHAs, hover effects, or complex UI states). Prefer mac.mouse.* for
physical interaction when possible.
| Command | Description |
|---|---|
/elements | List all interactive elements with coordinates, xpaths, roles |
/click <xpath> | Click element by xpath (JS click event) |
/read <xpath> [xpath2?] | Read text content of element(s) |
/fill <xpath> <text> | Type text into input field (JS event — prefer mac.key.type for real keystrokes) |
/hover <xpath> | Hover element (JS event) |
/scroll <xpath> <xOffset> <yOffset> | Scroll element by offset |
/xpath <x> <y> | Get xpath at normalized screen coordinates |
/source | Get full HTML source of current tab |
| Command | Description |
|---|---|
/clipboard.write <text> | Write to clipboard (may need Chrome permission grant first time) |
/copy <xpath> [range] [regex] | Copy text from element to clipboard |
/paste <xpath> [index] [regex] | Paste clipboard into element |
| Command | Description |
|---|---|
/screenshot | Capture current Chrome tab (also waits for DOM to settle) |
/ss.save | Save last screenshot to disk |
/ss.last | Return Base64 of last screenshot |
/ss.pixel <x> <y> | Get pixel color from last screenshot |
/mac.screenshot | Capture full macOS screen |
| Command | Description |
|---|---|
/query <prompt> | AI locates text on page by natural language |
/ai.llm <model> <text> | Send prompt to LLM (model must be gemini) |
/ai.do <model> <ask> | AI performs simple actions on current page |
/ai.ask <model> <goal> | AI performs actions until goal is met |
| Command | Description |
|---|---|
/echo <text> | Echo text back to console |
/store <varname> | Store last command result to variable |
/store.tab <alias> | Store last tab ID as alias |
/set <var> <text> | Assign var = text |
/eval <var> <formula> | Math evaluation |
/cmp <exp1> <op> <exp2> | Compare two expressions |
/extract <varname> <pattern> | Traverse JSON array by index pattern |
/csv.import <varname> | Import CSV file |
| Command | Description |
|---|---|
/arr.get <varname> <selector> | Get element by selector |
/arr.push <varname1> <varname2> | Push to array |
/arr.pop <varname> | Pop from array |
/arr.len <lenVar> <jsonVar> | Array length |
| Command | Description |
|---|---|
/record start <name> | Start recording workflow |
/record stop | Stop and save script |
/run <@me/name> [flag] [tabIds...] | Run saved script |
/fork <script> | Copy a script |
/schedule <@me/name> <schedule|off> | Schedule script execution |
/note <show|hide> [text] | Show/hide notes on controlled tab |
| Command | Description |
|---|---|
/for <var> <a> <b> { /cmd1; /cmd2 } | For loop |
/func <varName> <jsCode> | Run JS function on vars |
/ask <options...> <question> | Ask question with multiple choices |
/wait.element.found <selector> | Wait for element to appear |
/wait.element.gone <selector> | Wait for element to disappear |
/wait.element.clicked <selector> | Wait for element click |
| Command | Description |
|---|---|
/fetch <method> <url> [body] | HTTP request from Chrome context |
/store.url <variable> | Store current URL to variable |
| Command | Description |
|---|---|
/chromeoffset <y> | Set Chrome toolbar offset for mouse coords |
/log <enable|disable|clear> | Toggle verbose logging |
When /elements returns data, each element contains:
| Field | Description |
|---|---|
x, y | Normalized (0–1) viewport position relative to page width/height |
left, top | Absolute pixel position within Chrome content area |
width, height | Element dimensions in pixels |
cx, cy | Center point (pixel or normalized depending on source) |
xpath | Full XPath selector for use with /click, /read, /fill |
tag | HTML tag name |
role | ARIA role |
text | Visible text content |
isClickable | Whether element appears interactive |
Get center coordinates for mac.mouse.click:
center_x = left + width / 2
center_y = top + height / 2
WRONG: /mac.key.type hello world
CORRECT: /mac.mouse.click [cx] [cy] or in some cases /mac.mouse.dblclick [x] [y] sleep 1 /mac.key.shortcut cmd a sleep 1 /mac.key.shortcut backspace sleep 1 /mac.key.type hello world
/mac.mouse.click [cx] [cy] /mac.key.shortcut cmd a /mac.key.shortcut backspace
/mac.mouse.move [cx] [cy] 100
/mac.mouse.dblclick [cx] [cy]
/mac.mouse.click [cx] [cy] 5000 sleep 3 /mac.key.type ~/path/to/folder sleep 2 /mac.key.enter /mac.key.shortcut cmd a /mac.key.type filename-and-its.extension sleep 2 /mac.key.enter another /mac.key.enter * to make sure it replace existing file
/tab [full url]
/tab [tabId] sleep 1 /focus
/tab [tabId] sleep 1 /tab close
# 1. Focus Chrome, go to Console tab (this is the Mac viewer)
tahcia "key" "/mac.focus com.google.Chrome"
sleep 1
tahcia "key" "/tab console" # Note: lowercase "console" (case-sensitive!)
sleep 2
# 2. NOW focus the target app (brings it to front on the Mac)
tahcia "key" "/mac.focus com.apple.Keynote" # or whatever app
sleep 2
# 3. Take screenshot from Console tab (captures whatever is on Mac screen)
tahcia "key" "/screenshot"
sleep 2
tahcia "key" "/ss.last"
KEY RULES:
/ss.save before typing — the save dialog takes time to render/Users/ideerge/Downloads/name.png) — avoids needing to find/click the filename field/mac.mouse.click <x> <y> 50 → /mac.key.shortcut cmd a) to select all text. This is more reliable than double-click./mac.key.enter)/screenshot → /ss.last → extract base64 → call Gemini/other vision API directly from openclaw (NOT through Tahcia)
--
/elements fresh before any interaction/elements every time — page state changes constantly; user may have used the laptop between messages/click is a JS event — doesn't trigger real browser behavior. Use mac.mouse.click for physical clicks/hover is a JS event — use mac.mouse.move for physical hover/ss.save before typing/mac.focus com.google.Chrome → /tab <N> → /focus before page interaction"Loading..." indicators to disappearmac.mouse.drag is for text selection — mousedown → move → mouseup. Not for scrollingmac.mouse.click and mac.mouse.dblclick need 3rd param (hold time in ms) — this lets the cursor physically move to the target before clickingmac.mouse.* commands look human/mac.key.shortcut cmd a → /mac.key.shortcut backspace/mac.ocr returns x,y at a scale factor. Actual desktop click coords are x * scale, y * scale. Example: x:378, y:154, scale:2 → click at (756, 308)/mac.mouse.click <x> <y> 50 → /mac.key.shortcut cmd a). Double-click is for Keynote editing, not text fields./mac.focus — verify the target app actually gained focus before proceeding (Terminal may steal it)/mac.windows to check app window positions before using mac.mouse.* commands — windows may have moved/ss.last captures whatever the last /screenshot took — if on Console tab, it captures the Console page (which includes embedded Mac view). To capture a specific app, focus it first, then /screenshot from Console tab./mac.ocr returns coordinates in ABSOLUTE desktop pixelsscale factortahcia-mac not foundThe helper binary needs execute permissions:
chmod +x /usr/local/Cellar/tahcia/<version>/bin/tahcia-mac
tahcia-mac runsdone:1 without data/mac.screenshot) don't return data to the CLI — data goes to the Console tab# Get script
curl -H "Authorization: Bearer $API_KEY" \
"https://api.tahcia.com/scripts/@me/<hash>"
# Update script
curl -X PUT -H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"code": [...], "screenshots": [...]}' \
"https://api.tahcia.com/scripts/<hash>"
# Create session
curl -X POST "https://api.tahcia.com/session" \
-H "Authorization: Bearer $API_KEY"
The CLI one-shot handler outputs every WebSocket message as JSON:
{"text": "/command", "to": "cli", "from": "console"} — command echo{"type": "json", "data": [...], ...} — structured response (elements, etc.){"type": "mouse", "action": "click", "x": ..., "y": ..., "done": 1} — mac action result{"done": 1, "error": 0, ...} — command completeWait for done: 1 before sending the next command.
/screenshot vs /mac.screenshot/screenshot — captures the current Chrome TAB (not the Mac desktop). If on the Console tab, it captures the Console page (which includes an embedded Mac view)./mac.screenshot — captures the full macOS screen directly (returns Base64). Requires CLI installation./mac.ocr — operates on the Mac screen (not on the tab screenshot)./ss.save — saves the last /screenshot to disk (not /mac.screenshot)./console — return to Console tab without changing active tab in Tahcia./tab <N> — switch to tab by number./tab <url> — open new tab with URL./focus — make current tab active./mac.mouse.coord desktop — sets mouse to use absolute desktop coordinates./mac.mouse.coord chrome — sets mouse to use Chrome-relative coordinates.chrome (relative to Chrome window, not desktop).Chrome deprioritizes/unloads background tabs after ~30 seconds of inactivity. Since the Console tab's Mac view is a live stream of the desktop, it goes stale if you haven't visited it recently.
Always wake the Console tab before using the Mac view:
/mac.focus com.google.Chrome → /tab console — wakes the Console tab/mac.focus com.apple.Keynote/screenshot — now captures the live Mac view showing your app