← Commit history

grok-bridge 1.3.1: browser windows never count as the host app, grok_settings verb, grok_login presses Sign in, sign-in flow documented

John Lauer ·7e59f9fa62 ·6d ago ·parent ff2cd2f
12 files changed +132−25
BRIDGE_VERSION+1−1
@@ -1 +1 @@-1.3.0+1.3.1
CLAUDE.md+5−3
@@ -32,7 +32,7 @@ will seed/stream the runtime from the release zip via `updateManifestUrl` in `br   `ad_callback`. Never `shell_execute` / `pull_file` / `send_files` from the HTTP path. - `handlers/*.py` — readiness, describe, windows (open/screenshot/list), chat (status/read),   send, app (close/resize/record/login/logout/plugin), webhook, bots.-- `bridge.json` + `BRIDGE_VERSION` — KEEP IN LOCKSTEP (currently 1.3.0).+- `bridge.json` + `BRIDGE_VERSION` — KEEP IN LOCKSTEP (currently 1.3.1). - `adom-bridge-grok-manifest.json` — streaming manifest ab's `updateManifestUrl` points at;   its `url` is the RELEASE download URL of the zip, with matching `sha256`/`size`. - `SKILL.md` (root) — core USER skill, installed as **`grok`** (not `grok-bridge`).@@ -41,13 +41,15 @@ will seed/stream the runtime from the release zip via `updateManifestUrl` in `br - `package.json` — wiki skills pkg. Description is the skills-not-runtime sentence. Runtime   files and `*.zip` do **not** belong in `files[]`. -## Verbs (v1.3.0)+## Verbs (v1.3.1)  Existing: `grok_readiness` `grok_prewarm` `grok_describe` `grok_status` `grok_open` `grok_screenshot` `grok_list_windows` `grok_chat_status` `grok_chat_read` `grok_send` `grok_uninstall`.  App: `grok_close` `grok_resize` `grok_record_start` `grok_record_stop` `grok_record_status`-`grok_login` `grok_logout` `grok_plugin_install`.+`grok_login` `grok_logout` `grok_settings` `grok_plugin_install`.++Window picking: `_is_host_impostor` rejects ANY browser window (Edge/Chrome/Firefox/...), since sign-in tabs are titled "Sign in to Grok Bot"; exact title "Grok Bot" ranks first.  Webhook: `grok_webhook_status` `grok_webhook_ensure` `grok_webhook_bind` `grok_webhook_fire` `grok_bot_create` `grok_channel_create` `grok_bot_list`.
SKILL.md+1−1
@@ -50,7 +50,7 @@ adom-bridge --ai-thread "<your thread>" grok_readiness '{}' | `grok_record_start` / `stop` / `status` | Window recording | **`reason` required** on start | grok-record | | `grok_webhook_ensure` / `bind` / `fire` / `status`, `grok_bot_*`, `grok_channel_create` | Wake this session, create bots/channels | never log the key | grok-webhook | | `grok_computer_screenshot` | Screenshot grok-bridge's computer (the agent box), not Grok Bot.exe. One verb; unique nonce; do not bash-poll | optional `nonce`, `timeoutSeconds` | grok-webhook |-| `grok_login` / `grok_logout` / `grok_plugin_install` | Sign-in UI + plugins | never type passwords | grok-account |+| `grok_login` / `grok_logout` / `grok_settings` / `grok_plugin_install` | Sign-in (presses splash Sign in), plan + billing (`page:usage`), plugins | never type passwords | grok-account | | `grok_uninstall` | Tells you to `bridge_uninstall {"name":"grok"}`. Does **not** kill Grok Bot or Cursor. | — | this skill |  `grok_describe` is the completeness hatch; the per-call `_hint` is what you read in practice.
adom-bridge-grok-manifest.json+4−4
@@ -1,10 +1,10 @@ {   "manifest_version": 1,   "name": "grok",-  "version": "1.3.0",-  "url": "https://wiki.adom.inc/download/adom/grok-bridge/1.3.0/adom-bridge-grok-v1.3.0.zip",-  "sha256": "4a2d2172d9987987b4737fb75df0a27a8e230f635608703a65e480787e4e8049",-  "size": 48242,+  "version": "1.3.1",+  "url": "https://wiki.adom.inc/download/adom/grok-bridge/1.3.1/adom-bridge-grok-v1.3.1.zip",+  "sha256": "09d480e98a27cec6c3ae6509f7de65de1e910d8165febca6d399e6d33e3df3b5",+  "size": 49640,   "verbPrefixes": [     "grok_"   ],
bridge.json+3−2
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "grok",   "displayName": "Grok Bot",-  "version": "1.3.0",+  "version": "1.3.1",   "description": "Drive Grok Bot (standalone Electron app, Grok Bot.exe) from Adom Bridge: open/close/resize/screenshot/record the app, read thinking vs idle and the visible transcript of the focused thread, queue messages, bind the Grok Bridge webhook and create bots/channels. No public Grok HTTP API \u2014 the runtime talks back to ab over desktop_* callbacks. Cursor is last-resort fallback only if Grok Bot.exe is missing.",   "homepage": "https://wiki.adom.inc/adom/grok-bridge",   "author": "Adom Inc.",@@ -49,7 +49,8 @@     "grok_channel_create",     "grok_bot_list",     "grok_install",-    "grok_install_status"+    "grok_install_status",+    "grok_settings"   ],   "statusVerb": "grok_readiness",   "timeouts": {
dev-skills/grok-publish/SKILL.md+1−1
@@ -23,7 +23,7 @@ Wiki: https://wiki.adom.inc/adom/grok-bridge. Changelog / `-m` must be ≥10 cha - Skills library / README / hero: bump **`package.json` version** (pkg). Leave `BRIDGE_VERSION` + `bridge.json` version alone if the Python runtime did not change. - New/changed `grok_*` verbs: bump **all three** (`BRIDGE_VERSION`, `bridge.json`, `package.json`) in lockstep, rebuild the zip, upload release, rewrite manifest, then pkg publish. -Current runtime: **1.3.0**. Skills-only bumps `package.json` only; verb changes bump zip + manifest + package.json together.+Current runtime: **1.3.1**. Skills-only bumps `package.json` only; verb changes bump zip + manifest + package.json together.  ## Skills pkg allowlist 
handlers/app.py+71−5
@@ -1,4 +1,4 @@-"""App-window verbs: close / resize / record / login / logout / plugin_install.+"""App-window verbs: close / resize / record / login / logout / settings / plugin_install.  All driving is caller_identity.ad_callback (desktop_*). Never shell_execute / pull_file / send_files. Prefer hide over process_kill. Never kill Cursor.@@ -274,12 +274,41 @@ def _open_deeplink(url: str) -> dict:     return launch_host([url])  +SIGN_IN_STEPS = (+    "Sign-in is xAI's page ('Log into your Grok Bot account': Google / X / Apple / GitHub / email) in the OS "+    "default browser, then Cursor's 'Enable data sharing' (leave Share Data off, press Continue), then "+    "'Sign in to Grok Bot' with the account (press Sign in), then the browser's 'This site is trying to open "+    "Grok Bot' prompt (press Open). Account-chooser rows are not UIA-invokable: use a foregrounded "+    "desktop_click on the row only when the PC is idle (desktop_user_idle_ms). Never type passwords. "+    "Afterwards the app may say 'Setting up Grok Bot's computer', then 'having trouble connecting' or "+    "'Couldn't load Bots' for several minutes; wait, restart the app once, and only press Recover computer "+    "if the user says the computer's installed apps can go (it removes them)."+)++ def handle_login(_args: dict) -> dict:-    """Open Grok Bot settings in the background. Do NOT type passwords."""+    """Start sign-in. Signed-out splash: press its Sign in button (background UIA). Never types passwords."""+    hwnd, _win = grok_hwnd(allow_cursor=False)+    pressed = False+    press_res = None+    if hwnd:+        press_res = _cb("desktop_ui_click", {"hwnd": hwnd, "name": "Sign in"})+        pressed = _callback_ok(press_res) and not (press_res or {}).get("error")+    if pressed:+        return {+            "success": True,+            "pressedSignIn": True,+            "hwnd": hwnd,+            "typedPassword": False,+            "windowEtiquette": "background",+            "_hint": "Pressed Sign in on the Grok Bot splash (background UIA). The app now says 'Continue in your browser'. " + SIGN_IN_STEPS,+            "_next": "desktop_list_windows {} to find the browser tab, then desktop_screenshot_window on it",+        }     res = _open_deeplink(SETTINGS_DEEPLINK)     ok = _callback_ok(res)     return {         "success": ok or True,  # opening settings is best-effort; the human signs in+        "pressedSignIn": False,         "opened": ok,         "url": SETTINGS_DEEPLINK,         "background": True,@@ -287,10 +316,47 @@ def handle_login(_args: dict) -> dict:         "callback": res,         "windowEtiquette": "background",         "_hint": (-            "Launched Grok Bot.exe with grokbot://app/v1/settings?id=theme as an arg in the BACKGROUND (desktop_open_url refuses grokbot://; "-            "no password typing). On the account card, the human should click "-            "**Sign In with Cursor**. This verb will not type credentials."+            "No signed-out splash found (already signed in, or the window is not up: grok_open first). Opened "+            "Grok Bot settings via grokbot://app/v1/settings?id=theme in the BACKGROUND. " + SIGN_IN_STEPS+        ),+    }+++SETTINGS_PAGES = {"general": "general", "computer": "computer", "usage": "usage", "billing": "usage",+                  "updates": "updates", "theme": "theme"}+++def handle_settings(args: dict) -> dict:+    """Open a Grok Bot Settings page (General / Computer / Usage & Billing / Updates) in the background."""+    page = str(args.get("page") or args.get("id") or "usage").strip().lower()+    sid = SETTINGS_PAGES.get(page)+    if sid is None:+        return {+            "success": False,+            "errorCode": "bad_args",+            "error": f"unknown settings page {page!r}",+            "_hint": "page is one of general, computer, usage (Usage & Billing: plan, weekly limit, on-demand cap), updates.",+            "_next": "grok_settings {\"page\":\"usage\"}",+        }+    url = f"grokbot://app/v1/settings?id={sid}"+    res = _open_deeplink(url)+    ok = _callback_ok(res)+    return {+        "success": ok,+        "opened": ok,+        "page": page,+        "url": url,+        "windowEtiquette": "background",+        "callback": res,+        "_hint": (+            (f"Opened Grok Bot Settings > {page} in the background. " if ok else+             f"Could not launch Grok Bot with {url} (see callback); is it installed? grok_readiness. ")+            + "Read it with grok_screenshot. "+            "Usage & Billing shows the plan (e.g. 'Current plan: Pro'), the weekly usage % and reset, and the "+            "on-demand monthly cap billed through Cursor. Close the overlay with its X before typing in the chat."         ),+        "_next": "grok_screenshot {}",+        "related": ["grok_screenshot", "grok_login"],     }  
handlers/describe.py+7
@@ -266,6 +266,13 @@ VERB_CATALOG = {         "related": ["grok_webhook_ensure", "grok_bot_create"],         "pitfalls": ["errorCode webhook_not_bound if unbound"],     },+    "settings": {+        "summary": "Open a Grok Bot Settings page: general / computer / usage (plan + limits) / updates. Background.",+        "long": False,+        "hint": "grok_settings {page}. usage answers 'what plan am I on / what am I paying'. Then grok_screenshot.",+        "related": ["grok_screenshot", "grok_login"],+        "pitfalls": ["The overlay covers the chat; close it before typing a prompt."],+    },     "install": {         "summary": "Install Grok Bot on this PC from its public update feed. Background job, per-user, no admin, no login.",         "long": True,
handlers/windows.py+14−4
@@ -196,18 +196,28 @@ def _title_of(win: dict | None) -> str:     return str(win.get("title") or win.get("name") or "")  +# Browser windows whose tab title mentions Grok Bot (the sign-in pages "Sign in to+# Grok Bot", "Return to Grok Bot", the wiki page) are NOT the host app.+_BROWSER_MARKERS = (+    "google chrome", "microsoft edge", "microsoft\u200b edge", "mozilla firefox",+    "brave", "opera", "vivaldi", "chrome for testing", "ai-grok-bot",+)++ def _is_host_impostor(title: str) -> bool:-    """Google Chrome / ai-grok-bot are NOT the Grok Bot host."""+    """Any browser window (Chrome / Edge / Firefox / ...) or ai-grok-bot is NOT the Grok Bot host."""     tl = (title or "").lower()-    return "google chrome" in tl or "ai-grok-bot" in tl+    return any(m in tl for m in _BROWSER_MARKERS)   def _title_rank(title: str) -> int:-    tl = (title or "").lower()+    tl = (title or "").strip().lower()     if _is_host_impostor(title):         return 9-    if "grok bot" in tl:+    if tl == "grok bot":         return 0+    if "grok bot" in tl:+        return 1     if "cursor" in tl:         return 2     return 9
package.json+6−2
@@ -1,6 +1,6 @@ {   "slug": "grok-bridge",-  "version": "1.3.0",+  "version": "1.3.1",   "type": "app",   "description": "Grok Bridge lets Adom talk to Grok Bot. Adom's AI environment is for electrical engineers: any provider (Claude, Codex, Grok, Gemini, …), any desktop power tool, through Adom Bridge. grok_* open/close/resize/screenshot/record the app, read the thread, and create bots/channels via webhook.",   "keywords": [@@ -93,7 +93,11 @@     "install grok bot",     "grok_install",     "install grok bot on another pc",-    "download grok bot"+    "download grok bot",+    "grok_settings",+    "what plan am i on grok",+    "grok billing",+    "grok bot plan"   ],   "sample_prompts": [     {
server.py+3
@@ -32,6 +32,7 @@ from handlers.app import (     handle_record_status,     handle_record_stop,     handle_resize,+    handle_settings, ) from handlers.bots import handle_bot_create, handle_bot_list, handle_channel_create from handlers.describe import handle_describe@@ -79,6 +80,7 @@ KNOWN_VERBS = [     "grok_computer_screenshot",     "grok_install",     "grok_install_status",+    "grok_settings",     "grok_run_status",     "grok_bot_create",     "grok_channel_create",@@ -123,6 +125,7 @@ COMMAND_HANDLERS = {     "computer_screenshot": handle_computer_screenshot,     "install": handle_install,     "install_status": handle_install_status,+    "settings": handle_settings,     "run_status": handle_run_status,     "bot_create": handle_bot_create,     "channel_create": handle_channel_create,
skills/grok-account/SKILL.md+16−2
@@ -1,7 +1,7 @@ --- name: grok-account user-invocable: true-description: "Sign in / sign out of Grok Bot and install plugins without typing passwords. grok_login / grok_logout use the app UI (Sign In with Cursor / Sign Out). grok_plugin_install launches grokbot://app/v1/plugin/add?id=<id> as an exe arg because desktop_open_url refuses grokbot://. Trigger words: grok_login, grok_logout, grok_plugin_install, sign in grok bot, sign out grok bot, install grok plugin."+description: "Sign in / sign out of Grok Bot, read the plan and billing, and install plugins without typing passwords. grok_login presses the splash Sign in (xAI login page in the browser), grok_settings {page:usage} shows the plan, grok_logout uses Sign Out. grok_plugin_install launches grokbot://app/v1/plugin/add?id=<id> as an exe arg because desktop_open_url refuses grokbot://. Trigger words: grok_login, grok_logout, grok_settings, grok plan, what plan am i on, grok billing, grok usage limit, grok_plugin_install, sign in grok bot, sign out grok bot, install grok plugin." --- Parent skill: **grok** (the core driver). This file is the area split. @@ -20,7 +20,8 @@ adom-bridge --ai-thread "<your thread>" grok_plugin_install '{"id":"<plugin id>"  | CLI | Purpose | Key args | |---|---|---|-| `grok_login` | Launch Grok Bot.exe with `grokbot://app/v1/settings?id=theme` as an arg. Does **not** type passwords | — |+| `grok_login` | On the signed-out splash, presses **Sign in** (background UIA) and returns the browser steps. Otherwise opens settings. Never types passwords | — |+| `grok_settings` | Open a Settings page in the background: `general`, `computer`, `usage` (plan, weekly %, on-demand cap), `updates` | `page` | | `grok_logout` | Open settings; try UIA **Sign Out**. Else `needsHuman:true` | — | | `grok_plugin_install` | Launch Grok Bot.exe with `grokbot://app/v1/plugin/add?id=<id>` as an arg. Does not confirm install (the app UI does) | **`id` required** | @@ -33,3 +34,16 @@ Do not invent other grokbot:// paths. Do not scrape cookies to mint a session. `ok:true` means the UI opened, not that the human finished Sign In. If `needsHuman:true`, stop and tell them.  Webhook-driven plugin/login events: **grok-webhook**.++## The sign-in flow, as it is today (verified 2026-10-14, app 0.70.2)++1. `grok_login` presses **Sign in** on the splash. The app shows *Continue in your browser · Reopen link · Cancel* and opens the link in the **OS default browser**.+2. xAI's page, **Log into your Grok Bot account**: Google, X, Apple, GitHub or email. Pick the provider the account was made with (find the account first: Grok Bot mail comes from `[email protected]`).+3. Google's account chooser. Its rows are **not** UIA-invokable; a foregrounded `desktop_click` on the row is needed, so only do it when `desktop_user_idle_ms` says nobody is at the PC.+4. Cursor's **Enable data sharing** page: leave Share Data **off**, press **Continue**.+5. **Sign in to Grok Bot** with the account shown: press **Sign in** (only because you just opened it from the app).+6. The browser asks **This site is trying to open Grok Bot**: press **Open**.+7. The app shows *Setting up Grok Bot's computer*. On a new PC it can then say *Grok Bot is having trouble connecting* or *Couldn't load Bots* for 10 to 20 minutes. Wait, restart the app once (process tree of the main `Grok Bot.exe`), and check again. **Recover computer** removes the cloud computer's installed apps and packages for every bot on the account: only with the user's say-so.+8. A first sign-in may show *Introducing your primary Bot*: choose an existing bot (grok-bridge, the webhook mailbox) or create one.++Plan and billing: `grok_settings {"page":"usage"}` then `grok_screenshot`. It shows *Current plan* (Pro, Pro+, ...), the weekly usage limit and reset, and the on-demand monthly cap billed through Cursor.