app
Grok Bot
Public Made by Adomby adom
Grok Bridge lets Adom talk to Grok Bot. Adom's AI environment is for electrical engineers: any provider, any desktop power tool, one place.
← Commit history
Runtime 1.2.2 payload.agent relay on grok_webhook_fire
10 files changed
+75−19
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.2.0+1.2.2
CLAUDE.md+3−2@@ -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.2.0).+- `bridge.json` + `BRIDGE_VERSION` — KEEP IN LOCKSTEP (currently 1.2.2). - `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,7 +41,7 @@ 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.2.0)+## Verbs (v1.2.2) 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`.@@ -52,6 +52,7 @@ App: `grok_close` `grok_resize` `grok_record_start` `grok_record_stop` `grok_rec Webhook: `grok_webhook_status` `grok_webhook_ensure` `grok_webhook_bind` `grok_webhook_fire` `grok_bot_create` `grok_channel_create` `grok_bot_list`. `grok_webhook_ensure` scrapes UIA Edit **Webhook URL** / **Webhook key** (no human copy-paste).+`grok_webhook_fire` optional `agent` / `payload.agent` (name or id) relays into that teammate. Webhook bind file: `~/.adom/grok-webhook.json` `{url, key, header, boundAt}` (never log the key). `grok_webhook_ensure` reads UIA Edits Webhook URL / Webhook key (no OCR).
README.md+3−1@@ -56,7 +56,9 @@ Always pass `--ai-thread`. When more than one desktop is connected, pass `--targ A routine already named **Grok Bridge webhook** sits in the right pane, trigger **When a webhook fires**. Prefer `grok_webhook_ensure {}` — it reads the UIA Edit controls **Webhook URL** and **Webhook key** from that pane (no OCR, no human copy-paste) and writes `%USERPROFILE%\.adom\grok-webhook.json` on the PC. The key is never logged, never returned, never put in chat or this wiki. -`grok_webhook_fire` with `event: ping` is the smoke test. Bound events:+`grok_webhook_fire` with `event: ping` is the smoke test. Optional `agent` (teammate name or id) tells grok-bridge to **relay** into that bot's thread and paste the reply back here — there is still one mailbox (this routine). HTTP 200 is only a `runUuid`; poll `grok_chat_read` for a nonce in `payload`.++Bound events: | Event | What Grok Bot does | |---|---|
adom-bridge-grok-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "grok",- "version": "1.2.0",- "url": "https://wiki.adom.inc/download/adom/grok-bridge/1.2.0/adom-bridge-grok-v1.2.0.zip",- "sha256": "ef48c6f625d85f2785c58d9ea0142b8afe52b8013cbfaebb632490564bda2ba4",- "size": 37346,+ "version": "1.2.2",+ "url": "https://wiki.adom.inc/download/adom/grok-bridge/1.2.2/adom-bridge-grok-v1.2.2.zip",+ "sha256": "68431c556bffec619d75dfcdc246398f81a9cfe2bdf585c98b3862f56a88ee9a",+ "size": 37838, "verbPrefixes": [ "grok_" ],
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "grok", "displayName": "Grok Bot",- "version": "1.2.0",+ "version": "1.2.2", "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.",
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.2.0**. Do not republish the zip for a skills-only change.+Current runtime: **1.2.2**. Skills-only bumps `package.json` only; verb changes bump zip + manifest + package.json together. ## Skills pkg allowlist
handlers/describe.py+4−2@@ -222,13 +222,15 @@ VERB_CATALOG = { ], }, "webhook_fire": {- "summary": "POST {event, payload, source:grok-bridge} to the bound url. Auth is Authorization: Bearer, never a body key.",+ "summary": "POST {event, payload, source:grok-bridge, agent?} to the bound url. Auth is Authorization: Bearer, never a body key.", "long": False,- "hint": "Header Authorization: Bearer <key>. errorCode webhook_not_bound if unbound — grok_webhook_ensure first.",+ "hint": "Optional agent (name or id) as args.agent or payload.agent — grok-bridge relays into that teammate and pastes the reply into its own thread. Header Authorization: Bearer <key>. errorCode webhook_not_bound if unbound — grok_webhook_ensure first.", "related": ["grok_webhook_ensure", "grok_bot_create", "grok_channel_create", "grok_bot_list"], "pitfalls": [ "A JSON field named key is WRONG — auth is a header.", "Never log the sender key.",+ "There is still one mailbox (the Grok Bridge webhook routine). agent is a relay, not a second webhook.",+ "HTTP 200 is only a runUuid. Poll grok_chat_read for payload.nonce.", ], }, "bot_create": {
handlers/webhook.py+34−5@@ -201,6 +201,26 @@ def webhook_not_bound_error(verb: str) -> dict: } ++def _normalize_payload_agent(args: dict, payload):+ """Copy agent (name or id) from args or payload into payload.agent.++ The Grok Bridge webhook routine is the only mailbox. payload.agent tells+ grok-bridge to relay into that teammate's thread and paste the reply back.+ """+ pl = dict(payload) if isinstance(payload, dict) else {}+ raw = args.get("agent")+ if raw is None:+ raw = pl.get("agent") or pl.get("agentId") or pl.get("agent_id")+ if raw is None:+ return pl, None+ s = str(raw).strip()+ if not s:+ return pl, None+ pl["agent"] = s+ return pl, s++ def handle_webhook_fire(args: dict) -> dict: event = str(args.get("event") or "").strip() if not event:@@ -209,8 +229,9 @@ def handle_webhook_fire(args: dict) -> dict: "errorCode": "event_required", "error": "grok_webhook_fire needs {event}.", "_hint": (- "Pass event (and optional payload). Bound events: create_bot, create_channel, "- "list_bots — or any event the Grok Bridge webhook routine accepts."+ "Pass event (and optional payload). Optional agent (name or id) rides in "+ "payload.agent so grok-bridge relays into that teammate and pastes the reply "+ "back. Bound events: ping, create_bot, create_channel, list_bots." ), "_next": "grok_webhook_fire {\"event\":\"list_bots\"}", }@@ -222,6 +243,7 @@ def handle_webhook_fire(args: dict) -> dict: payload = args.get("payload") if payload is None: payload = {}+ payload, agent = _normalize_payload_agent(args, payload) try: hostname = socket.gethostname() except Exception:@@ -231,6 +253,8 @@ def handle_webhook_fire(args: dict) -> dict: "payload": payload, "source": SOURCE, }+ if agent:+ body["agent"] = agent if hostname: body["hostname"] = hostname body["desktop"] = hostname@@ -239,7 +263,7 @@ def handle_webhook_fire(args: dict) -> dict: headers = { "Content-Type": "application/json", "Accept": "application/json",- "User-Agent": f"{SOURCE}/1.2.0",+ "User-Agent": f"{SOURCE}/1.2.2", } key = cfg.get("key") or "" if key:@@ -302,14 +326,19 @@ def handle_webhook_fire(args: dict) -> dict: "urlHost": url_host(url), "hostname": hostname, }+ if agent:+ out["agent"] = agent if isinstance(parsed, dict): safe = {k: v for k, v in parsed.items() if k.lower() not in ("key", "senderkey", "token", "secret")} out["response"] = safe elif resp_text: out["bodyPreview"] = resp_text[:500] out["_hint"] = (- f"POSTed event={event} to webhook host {url_host(url)} (HTTP {status}). "- "Auth was header Authorization: Bearer, never a body field named key."+ f"POSTed event={event} to webhook host {url_host(url)} (HTTP {status})"+ + (f" for agent={agent!r}." if agent else ".")+ + " Auth was header Authorization: Bearer, never a body field named key. "+ "The HTTP response is only a runUuid; the answer lands in the grok-bridge "+ "thread — poll grok_chat_read for a nonce in payload." ) return out
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "grok-bridge",- "version": "1.2.1",+ "version": "1.2.2", "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, \u2026), 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": [
skills/grok-webhook/SKILL.md+23−1@@ -25,7 +25,7 @@ Never log the key. Never put the key in `_hint`. Auth on fire is header `Authori | `grok_webhook_status` | Whether the json has a url. Returns `bound`, `urlHost` (hostname only) | — | | `grok_webhook_ensure` | Scrape the routine pane and bind locally. No human copy-paste | optional `force` | | `grok_webhook_bind` | Write the json (local FS on the PC). `url` must be **https** | **`url`**, **`key`** |-| `grok_webhook_fire` | POST `{event, payload, source:"grok-bridge", hostname}` | **`event`**, optional `payload` |+| `grok_webhook_fire` | POST `{event, payload, source:"grok-bridge", hostname, agent?}` | **`event`**, optional `payload`, optional `agent` (name or id) | | `grok_bot_create` | Fire `event=create_bot` | **`name`**, optional `description` | | `grok_channel_create` | Fire `event=create_channel` | **`name`**, optional `member_ids` | | `grok_bot_list` | Fire `event=list_bots` | — |@@ -54,4 +54,26 @@ If unbound: `errorCode: webhook_not_bound` and `_next` is `grok_webhook_ensure { | `login_needed` | Ask the human to sign in | | `logout_done` | Confirm sign-out | ++## Named agent (relay, not a second mailbox)++The **Grok Bridge webhook** routine belongs to grok-bridge. `grok_webhook_fire` has no other destination — every POST still lands in the grok-bridge thread.++Pass `agent` (teammate **name** or **id**) as a top-level arg or `payload.agent` so grok-bridge **relays** into that bot's thread and pastes the reply back here for `grok_chat_read`:++```bash+adom-bridge --ai-thread "grok" --target AdomLapper grok_webhook_fire '{+ "event":"ping",+ "agent":"Adom Echo",+ "payload":{"nonce":"ECHO-ALIVE-8802","message":"say the nonce back"}+}'+```++- One match (name case-insensitive, or full/prefix id) → relay.+- Zero or several matches → grok-bridge says so in its thread; it does not guess.+- `create_bot` / `list_bots` / `create_channel` ignore `agent` (they are mailbox-owner actions).+- `grok_send {agent}` is still toast + inbox, not this loop.++HTTP 200 is only `{runUuid}`. Poll `grok_chat_read` for the nonce. Latency ~60–120s. Queue is FIFO.+ Smoke: `grok_webhook_fire {"event":"ping"}`. Verify with `grok_webhook_status` (hostname only).