diff --git a/.changeset/desktop-startup-passthrough.md b/.changeset/desktop-startup-passthrough.md new file mode 100644 index 0000000..8f07fbf --- /dev/null +++ b/.changeset/desktop-startup-passthrough.md @@ -0,0 +1,7 @@ +--- +"grok-bot-cli": patch +--- + +Pass Desktop startup configuration, sign-out, and CLI help/version requests through to the real Codex CLI instead of the interactive daemon bridge. + +Allow fallback when the bridge reaches EOF without consuming input or committing output. Existing shim users must re-run `gbot codex desktop-shim install` after upgrading, then fully quit and relaunch ChatGPT. diff --git a/README.md b/README.md index be61707..0cddfeb 100644 --- a/README.md +++ b/README.md @@ -225,10 +225,12 @@ gbot codex desktop-shim uninstall # removes wrapper/bridge/LaunchAgent; Desktop Install copies the scripts out of the package into durable `~/.codex/bin/` paths, so uninstalling the npm package never leaves Desktop pointing into a deleted checkout. The wrapper exports install-time `CODEX_HOME` and derives the socket from it at runtime (`CODEX_APP_SERVER_SOCK`), so custom `CODEX_HOME` layouts work; `gbot` itself resolves the socket the same way (`CODEX_APP_SERVER_SOCK` wins, else `CODEX_HOME`), and `status` reports the effective path and which variable won. -Fail-open runs only before any stdin byte is consumed and no daemon payload was committed to stdout: the wrapper preflights the daemon socket (3 s deadline) and runs the bridge as a child — never `exec` — falling through to the real standalone `codex` for every non-`app-server` spawn, every preflight failure, and every pre-session bridge failure (exit 1), always on pristine stdio+stdout. The bridge's connect + WebSocket-upgrade handshake runs under one absolute monotonic deadline (`CODEX_BRIDGE_CONNECT_TIMEOUT`, default 10 s) requiring `HTTP/1.1 101` plus a matching `Sec-WebSocket-Accept` (a `200` fails even with a valid hash), and a first-response deadline (`CODEX_BRIDGE_FIRST_MESSAGE_TIMEOUT`, default 30 s) bounds connect-to-matching-daemon-response covering the first RPC — an unrelated notification or foreign response never satisfies it, only the matching response/error (or timeout) clears it — preflight (3 s) + upgrade (10 s) + first RPC (30 s) stays far below a daemon-lock hang, and preflight and upgrade leave stdin untouched. Reads after a successful init block with no timeout so healthy idle sessions survive silence; every mid-session socket send and stdout write runs under its own write budget (`CODEX_BRIDGE_IO_TIMEOUT`, default 30 s), so a wedged or non-reading peer cannot hang Desktop. The commit point is byte-precise on input OR output, committed before the stdout write: the bridge counts every stdin byte read (buffered readahead included, even an unforwarded blank line) and marks stdout used before writing any forwarded daemon payload via a bounded write, so the fallback only ever runs on truly pristine stdio+stdout. Buffered EOF-tail data flushes before the WS Close. A mid-session bridge failure (exit 2+) makes the wrapper exit promptly so Desktop reconnects; the fallback never runs on half-consumed stdin or dirty stdout. The wrapper never starts the daemon on the spawn path (a wedged daemon lock must not block Desktop) — upkeep belongs to the LaunchAgent login script and install, which share one `CODEX_HOME` for the GUI domain and the daemon they start; if the socket is absent, Desktop simply runs stock Codex until the daemon is started. +Fail-open runs only before any stdin byte is consumed and no daemon payload was committed to stdout: the wrapper preflights the daemon socket (3 s deadline) and runs the bridge as a child — never `exec` — passing one-shot startup configuration, sign-out, help, version, and non-`app-server` requests to the real standalone `codex`, and falling back on every preflight failure or pre-session bridge failure (exit 1), always on pristine stdio+stdout. The bridge's connect + WebSocket-upgrade handshake runs under one absolute monotonic deadline (`CODEX_BRIDGE_CONNECT_TIMEOUT`, default 10 s) requiring `HTTP/1.1 101` plus a matching `Sec-WebSocket-Accept` (a `200` fails even with a valid hash), and a first-response deadline (`CODEX_BRIDGE_FIRST_MESSAGE_TIMEOUT`, default 30 s) bounds connect-to-matching-daemon-response covering the first RPC — an unrelated notification or foreign response never satisfies it, only the matching response/error (or timeout) clears it — preflight (3 s) + upgrade (10 s) + first RPC (30 s) stays far below a daemon-lock hang, and preflight and upgrade leave stdin untouched. Reads after a successful init block with no timeout so healthy idle sessions survive silence; every mid-session socket send and stdout write runs under its own write budget (`CODEX_BRIDGE_IO_TIMEOUT`, default 30 s), so a wedged or non-reading peer cannot hang Desktop. The commit point is byte-precise on input OR output, committed before the stdout write: the bridge counts every stdin byte read (buffered readahead included, even an unforwarded blank line) and marks stdout used before writing any forwarded daemon payload via a bounded write, so the fallback only ever runs on truly pristine stdio+stdout. Buffered EOF-tail data flushes before the WS Close. A mid-session bridge failure (exit 2+) makes the wrapper exit promptly so Desktop reconnects; the fallback never runs on half-consumed stdin or dirty stdout. The wrapper never starts the daemon on the spawn path (a wedged daemon lock must not block Desktop) — upkeep belongs to the LaunchAgent login script and install, which share one `CODEX_HOME` for the GUI domain and the daemon they start; if the socket is absent, Desktop simply runs stock Codex until the daemon is started. Residual risks, stated honestly: requests without both matching thread and turn IDs remain unanswered; a Codex client must handle those requests. A daemon that speaks framing-valid but semantically unexpected JSON-RPC (unknown methods, id-less responses) is treated as transport; pins are to app-server schema 0.158.0. The bridge trusts the local control socket; a malicious local daemon could hold the session up to the stated budgets, not past them. +After upgrading `gbot`, existing shim users must re-run `gbot codex desktop-shim install` and fully quit and relaunch ChatGPT to refresh the installed scripts. EOF without any consumed input or committed output falls back to the real CLI instead of counting as a served session. + Current shim limitation: Desktop's spawn-time app-tools MCP `-c` overrides are not forwarded to the already-running managed daemon. No restoration path or app-tools parity has been demonstrated here; this is not a claim of permanent protocol impossibility. Fully quit and relaunch ChatGPT.app after install (or login) so it inherits `CODEX_CLI_PATH`. `status` reads the macOS GUI-domain value via `launchctl getenv` (what Desktop actually inherits) alongside the calling shell's value. LaunchAgent persistence is macOS-first; elsewhere install still writes the wrapper and bridge but leaves `CODEX_CLI_PATH` for you to export. `~/.codex/bin` holds scripts only — there is no extra revert note to clean up; revert is `gbot codex desktop-shim uninstall` plus this section. **ChatGPT Desktop over CDP (scaffold).** Prefer the Desktop UI surface via Chrome DevTools Protocol when you need more than the app-server socket. CDP is **local-only** (`127.0.0.1`); there is no remote transport. Relaunch the macOS app with a debug port (Electron fuses block Node inspect, not Chromium's flag): diff --git a/artifact/agent-bundle.compile-evidence.json b/artifact/agent-bundle.compile-evidence.json index c458485..44cf877 100644 --- a/artifact/agent-bundle.compile-evidence.json +++ b/artifact/agent-bundle.compile-evidence.json @@ -1 +1 @@ -{"assets":[{"externals":[{"externalType":"node-commonjs","issuers":["node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.production.js"],"kind":"builtin","request":"async_hooks","userRequest":"async_hooks"},{"externalType":"node-commonjs","issuers":["node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.production.js"],"kind":"builtin","request":"crypto","userRequest":"crypto"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/736.js"],"kind":"builtin","request":"node:async_hooks","userRequest":"node:async_hooks"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/302.js"],"kind":"builtin","request":"node:buffer","userRequest":"node:buffer"},{"externalType":"module","issuers":["src/cli/codex/bridge/run.tsx","src/core/app-session.js","src/core/codex-bridge.js","src/core/desktop-shim.js","src/core/relay/managed.js"],"kind":"builtin","request":"node:child_process","userRequest":"node:child_process"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","src/core/app-session.js","src/core/chatgpt-desktop/facade.ts","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/gateway.js","src/core/relay/control.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/store.js"],"kind":"builtin","request":"node:crypto","userRequest":"node:crypto"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","src/core/app-session.js","src/core/chatgpt-desktop/cdp-adapter.ts","src/core/codex-bridge.js","src/core/codex/conversation.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/history.js","src/core/store.js"],"kind":"builtin","request":"node:fs","userRequest":"node:fs"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/40.js","src/cli/skills/add.tsx","src/core/claude-channel.js","src/core/relay/managed.js"],"kind":"builtin","request":"node:fs/promises","userRequest":"node:fs/promises"},{"externalType":"module","issuers":["src/core/claude-channel.js","src/core/codex-bridge.js","src/core/relay/control.js"],"kind":"builtin","request":"node:net","userRequest":"node:net"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","src/core/app-session.js","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/history.js","src/core/relay/profile.js","src/core/sender-prefix.js","src/core/store.js"],"kind":"builtin","request":"node:os","userRequest":"node:os"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","src/cli/skills/add.tsx","src/core/app-session.js","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/history.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/store.js"],"kind":"builtin","request":"node:path","userRequest":"node:path"},{"externalType":"module","issuers":["src/core/history.js"],"kind":"builtin","request":"node:readline","userRequest":"node:readline"},{"externalType":"module","issuers":["src/core/relay/control.js"],"kind":"builtin","request":"node:string_decoder","userRequest":"node:string_decoder"},{"externalType":"module","issuers":[".agent-bundle-virtual/bin-gbot-flight-entry.mjs","src/core/relay/managed.js"],"kind":"builtin","request":"node:url","userRequest":"node:url"},{"externalType":"module","issuers":[".agent-bundle-virtual/bin-gbot-flight-entry.mjs"],"kind":"builtin","request":"node:worker_threads","userRequest":"node:worker_threads"},{"externalType":"node-commonjs","issuers":["node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.production.js"],"kind":"builtin","request":"stream","userRequest":"stream"},{"externalType":"node-commonjs","issuers":["node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-client.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-client.node.production.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.production.js"],"kind":"builtin","request":"util","userRequest":"util"}],"packages":["@agent-bundle/runtime","effect","react","react-dom","react-server-dom-rspack","rsc-markdown-stream","zod"],"path":"bin/gbot-flight.mjs","sha256":"5e2211f62cd7501ba36f45ef1fc6577f91804f2daf77054d7308759261ff5441"},{"externals":[{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/736.js"],"kind":"builtin","request":"node:async_hooks","userRequest":"node:async_hooks"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/302.js"],"kind":"builtin","request":"node:buffer","userRequest":"node:buffer"},{"externalType":"module","issuers":["src/cli/codex/bridge/run.tsx","src/core/app-session.js","src/core/codex-bridge.js","src/core/desktop-shim.js","src/core/relay/managed.js"],"kind":"builtin","request":"node:child_process","userRequest":"node:child_process"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","node_modules/agent-bundle/dist/667~1.js","src/core/app-session.js","src/core/chatgpt-desktop/facade.ts","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/gateway.js","src/core/relay/control.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/store.js"],"kind":"builtin","request":"node:crypto","userRequest":"node:crypto"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","node_modules/agent-bundle/dist/launch-env.js","node_modules/agent-bundle/dist/terminal-capability.js","src/core/app-session.js","src/core/chatgpt-desktop/cdp-adapter.ts","src/core/codex-bridge.js","src/core/codex/conversation.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/history.js","src/core/store.js"],"kind":"builtin","request":"node:fs","userRequest":"node:fs"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/40.js","node_modules/agent-bundle/dist/667~1.js","src/cli/skills/add.tsx","src/core/claude-channel.js","src/core/relay/managed.js"],"kind":"builtin","request":"node:fs/promises","userRequest":"node:fs/promises"},{"externalType":"module","issuers":["src/core/claude-channel.js","src/core/codex-bridge.js","src/core/relay/control.js"],"kind":"builtin","request":"node:net","userRequest":"node:net"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","src/core/app-session.js","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/history.js","src/core/relay/profile.js","src/core/sender-prefix.js","src/core/store.js"],"kind":"builtin","request":"node:os","userRequest":"node:os"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","node_modules/agent-bundle/dist/launch-env.js","src/cli/skills/add.tsx","src/core/app-session.js","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/history.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/store.js"],"kind":"builtin","request":"node:path","userRequest":"node:path"},{"externalType":"module","issuers":["src/core/history.js"],"kind":"builtin","request":"node:readline","userRequest":"node:readline"},{"externalType":"module","issuers":["src/core/relay/control.js"],"kind":"builtin","request":"node:string_decoder","userRequest":"node:string_decoder"},{"externalType":"module","issuers":[".agent-bundle-virtual/bin-gbot-0.mjs",".agent-bundle-virtual/bin-gbot-entry.mjs","src/core/relay/managed.js"],"kind":"builtin","request":"node:url","userRequest":"node:url"},{"externalType":"module","issuers":[".agent-bundle-virtual/bin-gbot-entry.mjs"],"kind":"builtin","request":"node:worker_threads","userRequest":"node:worker_threads"},{"externalType":"node-commonjs","issuers":["node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-client.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-client.node.production.js"],"kind":"builtin","request":"util","userRequest":"util"}],"packages":["@agent-bundle/runtime","agent-bundle","effect","react","react-dom","react-server-dom-rspack","rsc-markdown-stream","zod"],"path":"bin/gbot.mjs","sha256":"70ed28198daa2a204a649760e01fb60688d27f9aaac66de372e39ea0351a0cdd"},{"externals":[{"externalType":"module","issuers":["src/core/claude-channel.js"],"kind":"builtin","request":"node:crypto","userRequest":"node:crypto"},{"externalType":"module","issuers":["node_modules/agent-bundle/dist/launch-env.js"],"kind":"builtin","request":"node:fs","userRequest":"node:fs"},{"externalType":"module","issuers":["src/core/claude-channel.js"],"kind":"builtin","request":"node:fs/promises","userRequest":"node:fs/promises"},{"externalType":"module","issuers":["src/core/claude-channel.js"],"kind":"builtin","request":"node:net","userRequest":"node:net"},{"externalType":"module","issuers":["src/core/claude-channel.js"],"kind":"builtin","request":"node:os","userRequest":"node:os"},{"externalType":"module","issuers":["node_modules/agent-bundle/dist/launch-env.js","src/core/claude-channel.js"],"kind":"builtin","request":"node:path","userRequest":"node:path"},{"externalType":"module","issuers":["node_modules/@modelcontextprotocol/server/dist/shimsNode.mjs","node_modules/agent-bundle/node_modules/@modelcontextprotocol/server/dist/shimsNode.mjs"],"kind":"builtin","request":"node:process","userRequest":"node:process"},{"externalType":"module","issuers":[".agent-bundle-virtual/mcp-claude-channel-8029413c-1.mjs"],"kind":"builtin","request":"node:url","userRequest":"node:url"}],"packages":["@modelcontextprotocol/core","@modelcontextprotocol/server","agent-bundle","zod"],"path":"mcp/mcp-claude-channel-8029413c.mjs","sha256":"46c0e5abb42f0dcd1152ec9d6bf5fed42c3bcb109f89312bfaa9870ecc059fe3"},{"externals":[{"externalType":"node-commonjs","issuers":["node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.production.js"],"kind":"builtin","request":"async_hooks","userRequest":"async_hooks"},{"externalType":"node-commonjs","issuers":["node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.production.js"],"kind":"builtin","request":"crypto","userRequest":"crypto"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/736.js"],"kind":"builtin","request":"node:async_hooks","userRequest":"node:async_hooks"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/302.js"],"kind":"builtin","request":"node:buffer","userRequest":"node:buffer"},{"externalType":"module","issuers":["src/core/app-session.js","src/core/codex-bridge.js","src/core/desktop-shim.js","src/core/relay/managed.js"],"kind":"builtin","request":"node:child_process","userRequest":"node:child_process"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","src/core/app-session.js","src/core/chatgpt-desktop/facade.ts","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/gateway.js","src/core/relay/control.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/store.js"],"kind":"builtin","request":"node:crypto","userRequest":"node:crypto"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","src/core/app-session.js","src/core/chatgpt-desktop/cdp-adapter.ts","src/core/codex-bridge.js","src/core/codex/conversation.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/store.js"],"kind":"builtin","request":"node:fs","userRequest":"node:fs"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/40.js","src/core/claude-channel.js","src/core/relay/managed.js"],"kind":"builtin","request":"node:fs/promises","userRequest":"node:fs/promises"},{"externalType":"module","issuers":["src/core/claude-channel.js","src/core/codex-bridge.js","src/core/relay/control.js"],"kind":"builtin","request":"node:net","userRequest":"node:net"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","src/core/app-session.js","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/relay/profile.js","src/core/sender-prefix.js","src/core/store.js"],"kind":"builtin","request":"node:os","userRequest":"node:os"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","src/core/app-session.js","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/store.js"],"kind":"builtin","request":"node:path","userRequest":"node:path"},{"externalType":"module","issuers":["src/core/relay/control.js"],"kind":"builtin","request":"node:string_decoder","userRequest":"node:string_decoder"},{"externalType":"module","issuers":[".agent-bundle-virtual/mcp-grok-bot-b8c2461e-flight-entry.mjs","src/core/relay/managed.js"],"kind":"builtin","request":"node:url","userRequest":"node:url"},{"externalType":"module","issuers":[".agent-bundle-virtual/mcp-grok-bot-b8c2461e-flight-entry.mjs"],"kind":"builtin","request":"node:worker_threads","userRequest":"node:worker_threads"},{"externalType":"node-commonjs","issuers":["node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.production.js"],"kind":"builtin","request":"stream","userRequest":"stream"},{"externalType":"node-commonjs","issuers":["node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-client.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-client.node.production.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.production.js"],"kind":"builtin","request":"util","userRequest":"util"}],"packages":["@agent-bundle/runtime","agent-bundle","effect","react","react-dom","react-server-dom-rspack","rsc-markdown-stream","zod"],"path":"mcp/mcp-grok-bot-b8c2461e-flight.mjs","sha256":"2b55bb68ad9bb68c60be43623091ec1f59f143a94c7b877b7ef48ef2e53369cc"},{"externals":[{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/736.js"],"kind":"builtin","request":"node:async_hooks","userRequest":"node:async_hooks"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/302.js"],"kind":"builtin","request":"node:buffer","userRequest":"node:buffer"},{"externalType":"module","issuers":["src/core/app-session.js","src/core/codex-bridge.js","src/core/desktop-shim.js","src/core/relay/managed.js"],"kind":"builtin","request":"node:child_process","userRequest":"node:child_process"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","node_modules/@agent-bundle/runtime/dist/lineage.js","node_modules/agent-bundle/dist/667~1.js","node_modules/agent-bundle/dist/mcp-server-runtime.js","src/core/app-session.js","src/core/chatgpt-desktop/facade.ts","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/gateway.js","src/core/relay/control.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/store.js"],"kind":"builtin","request":"node:crypto","userRequest":"node:crypto"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","node_modules/agent-bundle/dist/launch-env.js","node_modules/agent-bundle/dist/terminal-capability.js","src/core/app-session.js","src/core/chatgpt-desktop/cdp-adapter.ts","src/core/codex-bridge.js","src/core/codex/conversation.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/store.js"],"kind":"builtin","request":"node:fs","userRequest":"node:fs"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/40.js","node_modules/agent-bundle/dist/667~1.js","src/core/claude-channel.js","src/core/relay/managed.js"],"kind":"builtin","request":"node:fs/promises","userRequest":"node:fs/promises"},{"externalType":"module","issuers":["src/core/claude-channel.js","src/core/codex-bridge.js","src/core/relay/control.js"],"kind":"builtin","request":"node:net","userRequest":"node:net"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","src/core/app-session.js","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/relay/profile.js","src/core/sender-prefix.js","src/core/store.js"],"kind":"builtin","request":"node:os","userRequest":"node:os"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","node_modules/agent-bundle/dist/launch-env.js","src/core/app-session.js","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/store.js"],"kind":"builtin","request":"node:path","userRequest":"node:path"},{"externalType":"module","issuers":["node_modules/agent-bundle/node_modules/@modelcontextprotocol/server/dist/shimsNode.mjs"],"kind":"builtin","request":"node:process","userRequest":"node:process"},{"externalType":"module","issuers":["src/core/relay/control.js"],"kind":"builtin","request":"node:string_decoder","userRequest":"node:string_decoder"},{"externalType":"module","issuers":[".agent-bundle-virtual/mcp-grok-bot-b8c2461e-1.mjs",".agent-bundle-virtual/mcp-grok-bot-b8c2461e-2.mjs","src/core/relay/managed.js"],"kind":"builtin","request":"node:url","userRequest":"node:url"},{"externalType":"module","issuers":["node_modules/agent-bundle/dist/mcp-server-runtime.js"],"kind":"builtin","request":"node:worker_threads","userRequest":"node:worker_threads"},{"externalType":"node-commonjs","issuers":["node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-client.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-client.node.production.js"],"kind":"builtin","request":"util","userRequest":"util"}],"packages":["@agent-bundle/runtime","@modelcontextprotocol/core","@modelcontextprotocol/server","agent-bundle","effect","react","react-dom","react-server-dom-rspack","rsc-markdown-stream","zod"],"path":"mcp/mcp-grok-bot-b8c2461e.mjs","sha256":"28b7e5a8163764a5b68d6ac1814c8cf9cbc9df38218468c7fd0a82cda5d8f740"},{"externals":[{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/state.js"],"kind":"builtin","request":"node:assert/strict","userRequest":"node:assert/strict"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/736.js"],"kind":"builtin","request":"node:async_hooks","userRequest":"node:async_hooks"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/302.js"],"kind":"builtin","request":"node:buffer","userRequest":"node:buffer"},{"externalType":"module","issuers":["src/core/app-session.js","src/core/codex-bridge.js","src/core/desktop-shim.js"],"kind":"builtin","request":"node:child_process","userRequest":"node:child_process"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/state/sqlite.js","src/core/app-session.js","src/core/codex-bridge.js","src/core/gateway.js","src/core/relay/codex.js","src/core/relay/control.js","src/core/relay/engine.js","src/core/relay/profile.js","src/core/relay/records.js","src/core/relay/state.js","src/core/store.js"],"kind":"builtin","request":"node:crypto","userRequest":"node:crypto"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/state/sqlite.js","src/core/app-session.js","src/core/codex-bridge.js","src/core/codex/conversation.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/relay/codex.js","src/core/store.js"],"kind":"builtin","request":"node:fs","userRequest":"node:fs"},{"externalType":"module","issuers":["src/core/relay/ownership.js","src/core/relay/state.js","src/core/relay/worker.js"],"kind":"builtin","request":"node:fs/promises","userRequest":"node:fs/promises"},{"externalType":"module","issuers":["src/core/codex-bridge.js","src/core/relay/control.js","src/core/relay/ownership.js","src/core/relay/worker.js"],"kind":"builtin","request":"node:net","userRequest":"node:net"},{"externalType":"module","issuers":["src/core/app-session.js","src/core/codex-bridge.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/relay/engine.js","src/core/relay/profile.js","src/core/sender-prefix.js","src/core/store.js"],"kind":"builtin","request":"node:os","userRequest":"node:os"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/state/sqlite.js","src/core/app-session.js","src/core/codex-bridge.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/relay/engine.js","src/core/relay/ownership.js","src/core/relay/profile.js","src/core/relay/state.js","src/core/store.js"],"kind":"builtin","request":"node:path","userRequest":"node:path"},{"externalType":"import","issuers":["src/core/relay/ownership.js"],"kind":"builtin","request":"node:sqlite","userRequest":"node:sqlite"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/state/sqlite.js"],"kind":"builtin","request":"node:sqlite","userRequest":"node:sqlite"},{"externalType":"module","issuers":["src/core/relay/control.js","src/core/relay/worker.js"],"kind":"builtin","request":"node:string_decoder","userRequest":"node:string_decoder"}],"packages":["@agent-bundle/runtime","effect","zod"],"path":"scripts/gbot-relay.mjs","sha256":"80bb1a3f22648ca3c7cf04cb238caa86711086261978833cd6494350ccda51be"}],"coverage":{"rewritable":false,"unobserved":["import()","require()","require.resolve(…)","createRequire(…)(…)","import.meta.resolve(…)"]},"policy":{"name":"closed-world-externals","revision":1},"producer":{"name":"agent-bundle","rspack":"2.2.8","version":"0.4.0"}} +{"assets":[{"externals":[{"externalType":"node-commonjs","issuers":["node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.production.js"],"kind":"builtin","request":"async_hooks","userRequest":"async_hooks"},{"externalType":"node-commonjs","issuers":["node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.production.js"],"kind":"builtin","request":"crypto","userRequest":"crypto"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/736.js"],"kind":"builtin","request":"node:async_hooks","userRequest":"node:async_hooks"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/302.js"],"kind":"builtin","request":"node:buffer","userRequest":"node:buffer"},{"externalType":"module","issuers":["src/cli/codex/bridge/run.tsx","src/core/app-session.js","src/core/codex-bridge.js","src/core/desktop-shim.js","src/core/relay/managed.js"],"kind":"builtin","request":"node:child_process","userRequest":"node:child_process"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","src/core/app-session.js","src/core/chatgpt-desktop/facade.ts","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/gateway.js","src/core/relay/control.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/store.js"],"kind":"builtin","request":"node:crypto","userRequest":"node:crypto"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","src/core/app-session.js","src/core/chatgpt-desktop/cdp-adapter.ts","src/core/codex-bridge.js","src/core/codex/conversation.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/history.js","src/core/store.js"],"kind":"builtin","request":"node:fs","userRequest":"node:fs"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/40.js","src/cli/skills/add.tsx","src/core/claude-channel.js","src/core/relay/managed.js"],"kind":"builtin","request":"node:fs/promises","userRequest":"node:fs/promises"},{"externalType":"module","issuers":["src/core/claude-channel.js","src/core/codex-bridge.js","src/core/relay/control.js"],"kind":"builtin","request":"node:net","userRequest":"node:net"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","src/core/app-session.js","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/history.js","src/core/relay/profile.js","src/core/sender-prefix.js","src/core/store.js"],"kind":"builtin","request":"node:os","userRequest":"node:os"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","src/cli/skills/add.tsx","src/core/app-session.js","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/history.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/store.js"],"kind":"builtin","request":"node:path","userRequest":"node:path"},{"externalType":"module","issuers":["src/core/history.js"],"kind":"builtin","request":"node:readline","userRequest":"node:readline"},{"externalType":"module","issuers":["src/core/relay/control.js"],"kind":"builtin","request":"node:string_decoder","userRequest":"node:string_decoder"},{"externalType":"module","issuers":[".agent-bundle-virtual/bin-gbot-flight-entry.mjs","src/core/relay/managed.js"],"kind":"builtin","request":"node:url","userRequest":"node:url"},{"externalType":"module","issuers":[".agent-bundle-virtual/bin-gbot-flight-entry.mjs"],"kind":"builtin","request":"node:worker_threads","userRequest":"node:worker_threads"},{"externalType":"node-commonjs","issuers":["node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.production.js"],"kind":"builtin","request":"stream","userRequest":"stream"},{"externalType":"node-commonjs","issuers":["node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-client.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-client.node.production.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.production.js"],"kind":"builtin","request":"util","userRequest":"util"}],"packages":["@agent-bundle/runtime","effect","react","react-dom","react-server-dom-rspack","rsc-markdown-stream","zod"],"path":"bin/gbot-flight.mjs","sha256":"09eb472cda4e154e2216b30f8e3f9d3ecbb349203320138d22a39354bf0ae42e"},{"externals":[{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/736.js"],"kind":"builtin","request":"node:async_hooks","userRequest":"node:async_hooks"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/302.js"],"kind":"builtin","request":"node:buffer","userRequest":"node:buffer"},{"externalType":"module","issuers":["src/cli/codex/bridge/run.tsx","src/core/app-session.js","src/core/codex-bridge.js","src/core/desktop-shim.js","src/core/relay/managed.js"],"kind":"builtin","request":"node:child_process","userRequest":"node:child_process"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","node_modules/agent-bundle/dist/667~1.js","src/core/app-session.js","src/core/chatgpt-desktop/facade.ts","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/gateway.js","src/core/relay/control.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/store.js"],"kind":"builtin","request":"node:crypto","userRequest":"node:crypto"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","node_modules/agent-bundle/dist/launch-env.js","node_modules/agent-bundle/dist/terminal-capability.js","src/core/app-session.js","src/core/chatgpt-desktop/cdp-adapter.ts","src/core/codex-bridge.js","src/core/codex/conversation.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/history.js","src/core/store.js"],"kind":"builtin","request":"node:fs","userRequest":"node:fs"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/40.js","node_modules/agent-bundle/dist/667~1.js","src/cli/skills/add.tsx","src/core/claude-channel.js","src/core/relay/managed.js"],"kind":"builtin","request":"node:fs/promises","userRequest":"node:fs/promises"},{"externalType":"module","issuers":["src/core/claude-channel.js","src/core/codex-bridge.js","src/core/relay/control.js"],"kind":"builtin","request":"node:net","userRequest":"node:net"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","src/core/app-session.js","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/history.js","src/core/relay/profile.js","src/core/sender-prefix.js","src/core/store.js"],"kind":"builtin","request":"node:os","userRequest":"node:os"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","node_modules/agent-bundle/dist/launch-env.js","src/cli/skills/add.tsx","src/core/app-session.js","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/history.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/store.js"],"kind":"builtin","request":"node:path","userRequest":"node:path"},{"externalType":"module","issuers":["src/core/history.js"],"kind":"builtin","request":"node:readline","userRequest":"node:readline"},{"externalType":"module","issuers":["src/core/relay/control.js"],"kind":"builtin","request":"node:string_decoder","userRequest":"node:string_decoder"},{"externalType":"module","issuers":[".agent-bundle-virtual/bin-gbot-0.mjs",".agent-bundle-virtual/bin-gbot-entry.mjs","src/core/relay/managed.js"],"kind":"builtin","request":"node:url","userRequest":"node:url"},{"externalType":"module","issuers":[".agent-bundle-virtual/bin-gbot-entry.mjs"],"kind":"builtin","request":"node:worker_threads","userRequest":"node:worker_threads"},{"externalType":"node-commonjs","issuers":["node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-client.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-client.node.production.js"],"kind":"builtin","request":"util","userRequest":"util"}],"packages":["@agent-bundle/runtime","agent-bundle","effect","react","react-dom","react-server-dom-rspack","rsc-markdown-stream","zod"],"path":"bin/gbot.mjs","sha256":"89ad2b22378d67bb85a9fa9f6e20ff4fe4ddd28d90f91827ca7839c2937198f1"},{"externals":[{"externalType":"module","issuers":["src/core/claude-channel.js"],"kind":"builtin","request":"node:crypto","userRequest":"node:crypto"},{"externalType":"module","issuers":["node_modules/agent-bundle/dist/launch-env.js"],"kind":"builtin","request":"node:fs","userRequest":"node:fs"},{"externalType":"module","issuers":["src/core/claude-channel.js"],"kind":"builtin","request":"node:fs/promises","userRequest":"node:fs/promises"},{"externalType":"module","issuers":["src/core/claude-channel.js"],"kind":"builtin","request":"node:net","userRequest":"node:net"},{"externalType":"module","issuers":["src/core/claude-channel.js"],"kind":"builtin","request":"node:os","userRequest":"node:os"},{"externalType":"module","issuers":["node_modules/agent-bundle/dist/launch-env.js","src/core/claude-channel.js"],"kind":"builtin","request":"node:path","userRequest":"node:path"},{"externalType":"module","issuers":["node_modules/@modelcontextprotocol/server/dist/shimsNode.mjs","node_modules/agent-bundle/node_modules/@modelcontextprotocol/server/dist/shimsNode.mjs"],"kind":"builtin","request":"node:process","userRequest":"node:process"},{"externalType":"module","issuers":[".agent-bundle-virtual/mcp-claude-channel-8029413c-1.mjs"],"kind":"builtin","request":"node:url","userRequest":"node:url"}],"packages":["@modelcontextprotocol/core","@modelcontextprotocol/server","agent-bundle","zod"],"path":"mcp/mcp-claude-channel-8029413c.mjs","sha256":"46c0e5abb42f0dcd1152ec9d6bf5fed42c3bcb109f89312bfaa9870ecc059fe3"},{"externals":[{"externalType":"node-commonjs","issuers":["node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.production.js"],"kind":"builtin","request":"async_hooks","userRequest":"async_hooks"},{"externalType":"node-commonjs","issuers":["node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.production.js"],"kind":"builtin","request":"crypto","userRequest":"crypto"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/736.js"],"kind":"builtin","request":"node:async_hooks","userRequest":"node:async_hooks"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/302.js"],"kind":"builtin","request":"node:buffer","userRequest":"node:buffer"},{"externalType":"module","issuers":["src/core/app-session.js","src/core/codex-bridge.js","src/core/desktop-shim.js","src/core/relay/managed.js"],"kind":"builtin","request":"node:child_process","userRequest":"node:child_process"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","src/core/app-session.js","src/core/chatgpt-desktop/facade.ts","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/gateway.js","src/core/relay/control.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/store.js"],"kind":"builtin","request":"node:crypto","userRequest":"node:crypto"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","src/core/app-session.js","src/core/chatgpt-desktop/cdp-adapter.ts","src/core/codex-bridge.js","src/core/codex/conversation.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/store.js"],"kind":"builtin","request":"node:fs","userRequest":"node:fs"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/40.js","src/core/claude-channel.js","src/core/relay/managed.js"],"kind":"builtin","request":"node:fs/promises","userRequest":"node:fs/promises"},{"externalType":"module","issuers":["src/core/claude-channel.js","src/core/codex-bridge.js","src/core/relay/control.js"],"kind":"builtin","request":"node:net","userRequest":"node:net"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","src/core/app-session.js","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/relay/profile.js","src/core/sender-prefix.js","src/core/store.js"],"kind":"builtin","request":"node:os","userRequest":"node:os"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","src/core/app-session.js","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/store.js"],"kind":"builtin","request":"node:path","userRequest":"node:path"},{"externalType":"module","issuers":["src/core/relay/control.js"],"kind":"builtin","request":"node:string_decoder","userRequest":"node:string_decoder"},{"externalType":"module","issuers":[".agent-bundle-virtual/mcp-grok-bot-b8c2461e-flight-entry.mjs","src/core/relay/managed.js"],"kind":"builtin","request":"node:url","userRequest":"node:url"},{"externalType":"module","issuers":[".agent-bundle-virtual/mcp-grok-bot-b8c2461e-flight-entry.mjs"],"kind":"builtin","request":"node:worker_threads","userRequest":"node:worker_threads"},{"externalType":"node-commonjs","issuers":["node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.production.js"],"kind":"builtin","request":"stream","userRequest":"stream"},{"externalType":"node-commonjs","issuers":["node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-client.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-client.node.production.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-server.node.production.js"],"kind":"builtin","request":"util","userRequest":"util"}],"packages":["@agent-bundle/runtime","agent-bundle","effect","react","react-dom","react-server-dom-rspack","rsc-markdown-stream","zod"],"path":"mcp/mcp-grok-bot-b8c2461e-flight.mjs","sha256":"f2c4934e59fe73c8d7bd586c2c01adbabec0c7872b710134681e88532fdd128e"},{"externals":[{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/736.js"],"kind":"builtin","request":"node:async_hooks","userRequest":"node:async_hooks"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/302.js"],"kind":"builtin","request":"node:buffer","userRequest":"node:buffer"},{"externalType":"module","issuers":["src/core/app-session.js","src/core/codex-bridge.js","src/core/desktop-shim.js","src/core/relay/managed.js"],"kind":"builtin","request":"node:child_process","userRequest":"node:child_process"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","node_modules/@agent-bundle/runtime/dist/lineage.js","node_modules/agent-bundle/dist/667~1.js","node_modules/agent-bundle/dist/mcp-server-runtime.js","src/core/app-session.js","src/core/chatgpt-desktop/facade.ts","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/gateway.js","src/core/relay/control.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/store.js"],"kind":"builtin","request":"node:crypto","userRequest":"node:crypto"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","node_modules/agent-bundle/dist/launch-env.js","node_modules/agent-bundle/dist/terminal-capability.js","src/core/app-session.js","src/core/chatgpt-desktop/cdp-adapter.ts","src/core/codex-bridge.js","src/core/codex/conversation.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/store.js"],"kind":"builtin","request":"node:fs","userRequest":"node:fs"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/40.js","node_modules/agent-bundle/dist/667~1.js","src/core/claude-channel.js","src/core/relay/managed.js"],"kind":"builtin","request":"node:fs/promises","userRequest":"node:fs/promises"},{"externalType":"module","issuers":["src/core/claude-channel.js","src/core/codex-bridge.js","src/core/relay/control.js"],"kind":"builtin","request":"node:net","userRequest":"node:net"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","src/core/app-session.js","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/relay/profile.js","src/core/sender-prefix.js","src/core/store.js"],"kind":"builtin","request":"node:os","userRequest":"node:os"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/49.js","node_modules/agent-bundle/dist/launch-env.js","src/core/app-session.js","src/core/claude-channel.js","src/core/codex-bridge.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/store.js"],"kind":"builtin","request":"node:path","userRequest":"node:path"},{"externalType":"module","issuers":["node_modules/agent-bundle/node_modules/@modelcontextprotocol/server/dist/shimsNode.mjs"],"kind":"builtin","request":"node:process","userRequest":"node:process"},{"externalType":"module","issuers":["src/core/relay/control.js"],"kind":"builtin","request":"node:string_decoder","userRequest":"node:string_decoder"},{"externalType":"module","issuers":[".agent-bundle-virtual/mcp-grok-bot-b8c2461e-1.mjs",".agent-bundle-virtual/mcp-grok-bot-b8c2461e-2.mjs","src/core/relay/managed.js"],"kind":"builtin","request":"node:url","userRequest":"node:url"},{"externalType":"module","issuers":["node_modules/agent-bundle/dist/mcp-server-runtime.js"],"kind":"builtin","request":"node:worker_threads","userRequest":"node:worker_threads"},{"externalType":"node-commonjs","issuers":["node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-client.node.development.js","node_modules/react-server-dom-rspack/cjs/react-server-dom-rspack-client.node.production.js"],"kind":"builtin","request":"util","userRequest":"util"}],"packages":["@agent-bundle/runtime","@modelcontextprotocol/core","@modelcontextprotocol/server","agent-bundle","effect","react","react-dom","react-server-dom-rspack","rsc-markdown-stream","zod"],"path":"mcp/mcp-grok-bot-b8c2461e.mjs","sha256":"8d81d90e969e782a30e4571e0383d4460cf8d6a44616e0176fc13699cd50cb61"},{"externals":[{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/state.js"],"kind":"builtin","request":"node:assert/strict","userRequest":"node:assert/strict"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/736.js"],"kind":"builtin","request":"node:async_hooks","userRequest":"node:async_hooks"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/302.js"],"kind":"builtin","request":"node:buffer","userRequest":"node:buffer"},{"externalType":"module","issuers":["src/core/app-session.js","src/core/codex-bridge.js","src/core/desktop-shim.js"],"kind":"builtin","request":"node:child_process","userRequest":"node:child_process"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/state/sqlite.js","src/core/app-session.js","src/core/codex-bridge.js","src/core/gateway.js","src/core/relay/codex.js","src/core/relay/control.js","src/core/relay/engine.js","src/core/relay/profile.js","src/core/relay/records.js","src/core/relay/state.js","src/core/store.js"],"kind":"builtin","request":"node:crypto","userRequest":"node:crypto"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/state/sqlite.js","src/core/app-session.js","src/core/codex-bridge.js","src/core/codex/conversation.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/relay/codex.js","src/core/store.js"],"kind":"builtin","request":"node:fs","userRequest":"node:fs"},{"externalType":"module","issuers":["src/core/relay/ownership.js","src/core/relay/state.js","src/core/relay/worker.js"],"kind":"builtin","request":"node:fs/promises","userRequest":"node:fs/promises"},{"externalType":"module","issuers":["src/core/codex-bridge.js","src/core/relay/control.js","src/core/relay/ownership.js","src/core/relay/worker.js"],"kind":"builtin","request":"node:net","userRequest":"node:net"},{"externalType":"module","issuers":["src/core/app-session.js","src/core/codex-bridge.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/relay/engine.js","src/core/relay/profile.js","src/core/sender-prefix.js","src/core/store.js"],"kind":"builtin","request":"node:os","userRequest":"node:os"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/state/sqlite.js","src/core/app-session.js","src/core/codex-bridge.js","src/core/codex/remote-control.js","src/core/desktop-shim.js","src/core/relay/engine.js","src/core/relay/ownership.js","src/core/relay/profile.js","src/core/relay/state.js","src/core/store.js"],"kind":"builtin","request":"node:path","userRequest":"node:path"},{"externalType":"import","issuers":["src/core/relay/ownership.js"],"kind":"builtin","request":"node:sqlite","userRequest":"node:sqlite"},{"externalType":"module","issuers":["node_modules/@agent-bundle/runtime/dist/state/sqlite.js"],"kind":"builtin","request":"node:sqlite","userRequest":"node:sqlite"},{"externalType":"module","issuers":["src/core/relay/control.js","src/core/relay/worker.js"],"kind":"builtin","request":"node:string_decoder","userRequest":"node:string_decoder"}],"packages":["@agent-bundle/runtime","effect","zod"],"path":"scripts/gbot-relay.mjs","sha256":"360b4c348d3252054a396d9e903a4c6e277992a0f3bffe7cd580016816f585cd"}],"coverage":{"rewritable":false,"unobserved":["import()","require()","require.resolve(…)","createRequire(…)(…)","import.meta.resolve(…)"]},"policy":{"name":"closed-world-externals","revision":1},"producer":{"name":"agent-bundle","rspack":"2.2.8","version":"0.4.0"}} diff --git a/artifact/agent-bundle.manifest.json b/artifact/agent-bundle.manifest.json index 1b6729d..5bbb958 100644 --- a/artifact/agent-bundle.manifest.json +++ b/artifact/agent-bundle.manifest.json @@ -1 +1 @@ -{"application":{"description":"Message Grok Bot from Codex, Claude Code, and Cursor. Codex/Claude tools use local sockets on the user's registered machines only (no remote transport); from the Grok Bot box, run gbot via Grok Bot Shell with a machineId.","id":"plugin:gbot","name":"gbot","version":"0.12.4"},"compiler":{"adapters":[{"adapterRevision":"1.28.0","host":"claude","observedVersion":"2.1.260","schemas":[{"name":"hooks","revision":"2.1.260","sha256":"0cdfc5eb5201f2c3091768559ac82f4c563ccb1b7bce7a39d5f99e5f404654cb"},{"name":"lsp","revision":"2.1.260","sha256":"b4419c5d857267c7e2b21e3e1eb98b4fdc302c87109007190d3258e6ba7096e4"},{"name":"marketplace","revision":"2.1.260","sha256":"31ee4cc43ba5ce2be248030a69da2d31171550b2cc51c1fcb9f788a8ab92783d"},{"name":"mcp","revision":"2.1.260","sha256":"edd4770e41d6aee5beae1ff918d33139613a243e454ecbd52b72fa824be4a662"},{"name":"monitors","revision":"2.1.260","sha256":"6378d94b51fb7c784eaee178237008a03da0f889d0520d57486784954a1d484c"},{"name":"plugin","revision":"2.1.260","sha256":"2a976091b81ad07ae8eca57f6f9c5749efeba17fa089a0c12b6ca84d6e70f118"},{"name":"settings","revision":"2.1.260","sha256":"2bbca553621dbf9433a9b7d1ff7952543a368434f2d999a4e4e360b95b6d4c3d"},{"name":"theme","revision":"2.1.260","sha256":"9931264e6f5a1d4b3b854ce7a17d602c9ba1b57cc1991f1d3991b316eaa81ac2"}]},{"adapterRevision":"1.13.0","host":"codex","observedVersion":"0.147.0","schemas":[{"name":"app","revision":"0.147.0","sha256":"01c720a645e437bf0c4f8c26fd4cb5a13988e5649e4a8562ee23a1d4b7355c6a"},{"name":"hooks","revision":"0.147.0","sha256":"175b859eb8e85bd287d85ee840d97c3f5c2d0dda3223507a796d158e3770eeba"},{"name":"marketplace","revision":"0.147.0","sha256":"fbccce3ade39e1b077fcb440e60260de6e811e8c2d222b2ae0fe8fe47706b470"},{"name":"mcp","revision":"0.147.0","sha256":"75bd50f9fcb85c2e8d43bc132d61c172a02f28ea8bb77389816ae77b14a4257e"},{"name":"plugin","revision":"0.147.0","sha256":"074c6c71966a3e6560ccbceb8d82ec6a40cb1eccee2f2d863fb4ef1e2276a814"}]},{"adapterRevision":"1.13.0","host":"cursor","observedVersion":"2026-08-28","schemas":[{"name":"hooks","revision":"2026-08-28","sha256":"06154b7afa0861df462130b988912b897e7ccf962b8dd20c09193100bcde5d81"},{"name":"marketplace","revision":"2026-08-28","sha256":"1aae96a24c2796419933bc8bfe3a1255394e7199c35740b36325e0ce6dbc253d"},{"name":"mcp","revision":"2026-08-28","sha256":"f3fa4615afefe004c4fbcc09e635d890df0f1ec0cb39540feab72cbd3a31d844"},{"name":"plugin","revision":"2026-08-28","sha256":"a393b758901803fcf5cfe0d77bda8a83e987d32c3377dfce2d9edf445af884ed"}]},{"adapterRevision":"1.10.0","host":"portable","observedVersion":"1.0.0","schemas":[{"name":"mcp","revision":"1.0.0","sha256":"6539175bfcdf43085855183e86da40ea94b166547a72b47ae9a0a390516d3acb"},{"name":"plugin","revision":"1.0.0","sha256":"0a4aad95ce337878ad38802ebf0daa3fde76abe3f65400c86bcbb1ec0b3ab883"}]}],"agentSkills":{"schemaSha256":"6d06b61e423317421de2295ea1fd0c7b4491bfa36c5b7705c28616dfe76d4047","sourceRevision":"69ef37e9424c0a7ea9dd2293b559e43ec8176379","specification":"https://raw.githubusercontent.com/agentskills/agentskills/69ef37e9424c0a7ea9dd2293b559e43ec8176379/docs/specification.mdx"},"producer":{"name":"agent-bundle","version":"0.4.0"},"project":{"configDigest":"7718be28a54eb7ec9e28b9653ef671683604436af21e1eda99c11098f43e278f","configPath":"agent-bundle.config.ts","modelDigest":"3deade4a0e41ae311d1b34ee00195bb15ebc6b2c83fcfd67514b0f8f068c8b74","packageName":"grok-bot-cli","packageVersion":"0.12.4","revision":"39d201c2d3e2100f565d08f472c4cbbe3e6f7ffd8dd2b17afa9ef7a63a10e93e","sourceInputs":[{"executable":false,"path":".changeset/config.json","sha256":"0a97ce5d0488bada887ee9ea3726edecc727454b7da0294f1b5fe4947f48ee7c"},{"executable":false,"path":".changeset/mcp-server-patch.md","sha256":"446138a1bb0325fbe49a415709e09df47ea8e2775d3c4dc9c11725b116cd29d8"},{"executable":false,"path":".changeset/README.md","sha256":"495bd4805f8362853bce99689c1cd75db8c475dfa38930619d2e197d7554db27"},{"executable":false,"path":".github/workflows/changeset.yml","sha256":"0139beb8a3dc0e95126c9701d09251a879331b1e8c5540bbfc69848525b40f23"},{"executable":false,"path":".github/workflows/ci.yml","sha256":"17a58cf5ceff03b7922d15c9b0eea11ae3e0fd00a581ff6881f69cf795f3ec0b"},{"executable":false,"path":".github/workflows/release.yml","sha256":"9c97360a2221fca9317edcac0eb343298c765e3b34bd048f916c47533dd90311"},{"executable":false,"path":".gitignore","sha256":"30aaeac24084f0a642d811aa493fc51b0fd3eddff322d359c39c573d8394ff3e"},{"executable":false,"path":".npmrc","sha256":"da47110e4c2d7cd216cd495b604ca78bd0f5caaca32da4837ec0efa6f967bba0"},{"executable":false,"path":"agent-bundle.config.ts","sha256":"7718be28a54eb7ec9e28b9653ef671683604436af21e1eda99c11098f43e278f"},{"executable":false,"path":"AGENTS.md","sha256":"e18ce90a031c533e7226789b97f6bce4aa7994b672544e0f425d20cc3bc15d29"},{"executable":false,"path":"CHANGELOG.md","sha256":"8913d85a39ba09eee5b057c851115242dbbc31a573abf65d00724594fba7e1de"},{"executable":false,"path":"demo/grok-bot-cli-demo.gif","sha256":"0fe01e15fd2abbbc5cc3313e42931d404d3a320764e22fdcfb995f1a45dd73cd"},{"executable":false,"path":"demo/grok-bot-cli-demo.mp4","sha256":"b6ed66e43616674096c2f0a4a4da46fe4693c681833eab013f872a66b9ff933c"},{"executable":false,"path":"docs/chatgpt-desktop-cdp.md","sha256":"6dd6cfe00e9a888c1ced79f1db85744f9c92affef8a78126e22ea5205e76b732"},{"executable":false,"path":"docs/codex-busy-threads.md","sha256":"42509e8e4c2b519949f06d4e7028b8973d56f8fc0cd98decdb231375bb49d38d"},{"executable":false,"path":"docs/codex-duplex-architecture.md","sha256":"792f1be8bad508b042c5813c1fc3b7ea5ac87032803227830666727306e55fa2"},{"executable":false,"path":"docs/verification/2026-09-15-duplex.md","sha256":"c7d97f709a3c2d851c0a24690321bbda161ec925b4143a456b9951e174bf75df"},{"executable":false,"path":"LICENSE","sha256":"bc48c8a0f6b6e45842e574c79a6b955fe683ed13f48a50cc08bcccdb1a59fe59"},{"executable":false,"path":"package-lock.json","sha256":"f96cdb413a9939f2e0895fbc3fe855ef5da196d4e50354796978cade170e36fc"},{"executable":false,"path":"package.json","sha256":"39870136f51c0d1279cdf1deba3fae7f2e57b66a8119fa55753e01bfdb5b2cef"},{"executable":false,"path":"README.md","sha256":"f76022d94cf04e6f3dae28920fb9564e60716bb91965afa07822d709f4b36411"},{"executable":false,"path":"renovate.json","sha256":"e80b4e42a3043bc12fa0640db4bac392d2bf770acf841360d7c8ceeeac2ec1a9"},{"executable":false,"path":"rstest.route-unit.config.ts","sha256":"7286e20dc3d4162673e7c3dfe824db00307e1fda178410f23044a97286d141f2"},{"executable":false,"path":"scripts/run-unit-tests.mjs","sha256":"483f91b4856ce294a483dd2e62ca53bf09301adbc4c5fc9571da0e1d28049bc6"},{"executable":false,"path":"src/cli/_shared.ts","sha256":"555210759d9b2e2e60a01452c137abd73bad42bfab8b2311b66a3ab97f4ee5e9"},{"executable":false,"path":"src/cli/approvals/list.tsx","sha256":"8d21320b6cd9d6b2a266fd30196b8a439960f865a6f0a5dacf5845d64ed7388c"},{"executable":false,"path":"src/cli/approvals/respond.tsx","sha256":"7d9fe6e641deb08f444684733daac6da47fb472d90ed0e9ff77892f376a87255"},{"executable":false,"path":"src/cli/bots/create.tsx","sha256":"9f7a73a0595b642dd77f008ad596bcafd00fc5846ec1abff2ce62819d983eb84"},{"executable":false,"path":"src/cli/bots/delete.tsx","sha256":"126b53ac1da4a657f22d1a6770ddbab2cc66715c811de0873d50016bbe39cbf2"},{"executable":false,"path":"src/cli/bots/get.tsx","sha256":"f0c752a3b498819083a8ee5aa918d275727f1cc94d8774d8c84ac74db66e8a71"},{"executable":false,"path":"src/cli/bots/list.tsx","sha256":"21c0f7e39005860dfc2514b71f745bb4cab29d409ef2cff26f8759bb0fd261c1"},{"executable":false,"path":"src/cli/bots/update.tsx","sha256":"33b7f5aafbf931cf3cb5ecf423a76dacd09adb117a7d5490050639ae7f4210dd"},{"executable":false,"path":"src/cli/chatgpt-desktop/hosts.tsx","sha256":"64e3e873115a4f5c2b6a7fd68236dbf2ca4e4a880ba97bdd7409b48931bca632"},{"executable":false,"path":"src/cli/chatgpt-desktop/read.tsx","sha256":"11c86f604c2ee405f7544394db60ee1b36917d0b90769d441fcd361b5f0fe038"},{"executable":false,"path":"src/cli/chatgpt-desktop/search.tsx","sha256":"0dc3acdea178ff14d6c1c4b8b3bf1e18af47984f63b8ed8c41483e4da4b72f08"},{"executable":false,"path":"src/cli/chatgpt-desktop/send.tsx","sha256":"b801a028980e60fe37dca56e86887a1e2b0b8ce80ce77b379e7375d2e4544d5e"},{"executable":false,"path":"src/cli/chatgpt-desktop/status.tsx","sha256":"141b7b6db6d6f9236dd5343ca9495200752937b05764ad7fa7261631c2db5bbc"},{"executable":false,"path":"src/cli/chatgpt-desktop/threads.tsx","sha256":"d0a33935d992af349c52ef448622da5e460b4af1a9474aac60994295245b43ad"},{"executable":false,"path":"src/cli/claude/send.tsx","sha256":"d650caadec248aa59530d124ca6186c45d9f9fe1fb0f9c805e9eef6dc9f06d92"},{"executable":false,"path":"src/cli/codex/bridge/respond.tsx","sha256":"9d3a6af4904af6e44583dfb4ef317b3fe1045a78e765fc27406eaca9bcadcd13"},{"executable":false,"path":"src/cli/codex/bridge/run.tsx","sha256":"a49d334bebb00db62ad2c54370a47ec2c9119b4c07eb6ba11fc4bba4dd8e4217"},{"executable":false,"path":"src/cli/codex/bridge/start.tsx","sha256":"9e423cf5d65168606f6235baa2bcd127eb949647f7a4dee284d75f831474524d"},{"executable":false,"path":"src/cli/codex/bridge/status.tsx","sha256":"c801f69080985c87025429fb136487acdfac093acb07241b3417ea9a83915a18"},{"executable":false,"path":"src/cli/codex/bridge/stop.tsx","sha256":"6710e298a21d124c442d5aa1a3744280e5cd4df93213797f2ea3c0654174184a"},{"executable":false,"path":"src/cli/codex/desktop-shim.tsx","sha256":"fa3d88d11895bc4a15700bd590c055aa392b2b0e0c1185c52da233a23589aba0"},{"executable":false,"path":"src/cli/codex/list-threads.tsx","sha256":"6675fb6ef19acb63375bf47d302083d4bde055ce3dfc8c9db39664f3ec68b0f1"},{"executable":false,"path":"src/cli/codex/new.tsx","sha256":"11d0658c1a3e7173d53b71446b184fccccd867baefc6f733e43a9d543ca26ec9"},{"executable":false,"path":"src/cli/codex/queue.tsx","sha256":"7993ac5b827042cd95041775b183fde2d112b5ba7d5c82098e778a44e473826d"},{"executable":false,"path":"src/cli/codex/send.tsx","sha256":"7dbe3c79d5dcfbd9fd18b8bc93ca83d2187f86288e2d8beb8a314010c8c82360"},{"executable":false,"path":"src/cli/codex/status.tsx","sha256":"b8c31001cb2892c1bfccf502f9776ee06998eac4fc0356f92936fa2c34f272d8"},{"executable":false,"path":"src/cli/codex/wait.tsx","sha256":"0a4cc6c440ad496682031658bda375fee29f5c346d833618d4f143de29b351cd"},{"executable":false,"path":"src/cli/codex/watch.tsx","sha256":"3280f42d15941cfd00b3727d9e271972c39165bd28db8f56acca5664d777cb4d"},{"executable":false,"path":"src/cli/doctor.tsx","sha256":"c30ce4e57fe707d36de29700abf5a2c56834bb4fb3adf2a70bb1089b4150ddc8"},{"executable":false,"path":"src/cli/groups/add.tsx","sha256":"2a5de115852f0ca6a6638300bd5006862a02a2f1e2f527b6fd680f610c1d777a"},{"executable":false,"path":"src/cli/groups/create.tsx","sha256":"da3f2539f338063daf51b80d86c27b2752f0e4a0334078d11508e27981590fa3"},{"executable":false,"path":"src/cli/groups/delete.tsx","sha256":"2d4e91113a2c24cefefcfd464513918791592b5e8734a4ef901eabe42c9c7289"},{"executable":false,"path":"src/cli/groups/get.tsx","sha256":"efed88fb3ab7292d60d6532f1f966f784ba3376b7826c9f3e5028467f86241e9"},{"executable":false,"path":"src/cli/groups/list.tsx","sha256":"f3cd229d41dd389b767447a04267700ab2293eae87a878f7d88149cb5798f4a6"},{"executable":false,"path":"src/cli/groups/members.tsx","sha256":"9b9eaecb65ea995006eff1b24a287110d16bf4830a0b0d07221c2f1317853641"},{"executable":false,"path":"src/cli/groups/remove.tsx","sha256":"96dd85d1b9732681ce9dc126ac3ae562d3f287f4c28e36b9a8f8c301dc93f63d"},{"executable":false,"path":"src/cli/groups/set.tsx","sha256":"4f9d90db612f14ddbb6d2afa5e45b66f044aed661ff95415e567e1be3b6108c6"},{"executable":false,"path":"src/cli/groups/update.tsx","sha256":"f44305815ddb6e27286f97ef68c04cfa5548acd5f04b1aba10ee7ce3cdbfc851"},{"executable":false,"path":"src/cli/history.tsx","sha256":"5151b48113b4ba0516bacabfabcd48e3256ead807c34d914deb84f31ded0b062"},{"executable":false,"path":"src/cli/send.tsx","sha256":"47297793ba98380300c45951d4d36e68b0f74e2933a5b6ac6634c4b6708f5ce7"},{"executable":false,"path":"src/cli/skills/add.tsx","sha256":"fbde237d172c026d7912174e705c03cbc9d74dad7806fc58c6ceb43127c69ba6"},{"executable":false,"path":"src/cli/skills/list.tsx","sha256":"34f255687c69aaf598a1dc54220453b57c46ed9ab4a168648d9c7eca6535ebae"},{"executable":false,"path":"src/cli/skills/remove.tsx","sha256":"21662c80a62f276c2dfd840a328e9665c0c5f63959a239d4dd877372c46f60d0"},{"executable":false,"path":"src/cli/thread.tsx","sha256":"29ab775936fa2987cb789754a44888fc52bdaad07180c5471122dda508cfd0c2"},{"executable":false,"path":"src/commands/codex-send.md","sha256":"1f88961a6656ae94a5cadd86e25eeaae111ee4c6aaad64f32917ac0c9d8410b8"},{"executable":false,"path":"src/commands/codex-threads.md","sha256":"e17b90cb8b0115a53161f83797ec55ca65e81600396cf3ea896b6306d2fb2dd2"},{"executable":false,"path":"src/commands/codex-wait.md","sha256":"acd0aaf00329256b874ec59666d2d803bbaf86b1d249d996a5f226b559619530"},{"executable":false,"path":"src/core/app-session.js","sha256":"2b2eb53ec3f4c7ea0dccf269fa9720af6f1ce36b8ca36e889d2bb271a092717d"},{"executable":false,"path":"src/core/chatgpt-desktop/adapter.ts","sha256":"5a0a4f2cea9f1fc1a739c534aed3979c28e0bd9c057b8475e6251974ad44ed7a"},{"executable":false,"path":"src/core/chatgpt-desktop/app-server-fallback.ts","sha256":"44002096eea29b5ea26d5b3d186b5616fcdab570888e3cb86fd4b5c5804af04c"},{"executable":false,"path":"src/core/chatgpt-desktop/cdp-adapter.ts","sha256":"db60fdd3be0259a6c76cad7d8ff86eddc6acbbd21c5f20d6aeffff5390063efa"},{"executable":false,"path":"src/core/chatgpt-desktop/cdp-dom.ts","sha256":"a122fb76626006fa8dedb7088f9b5533a8058c3a58838692b168e0753b7c89eb"},{"executable":false,"path":"src/core/chatgpt-desktop/cdp-session.ts","sha256":"8d6512ee80b179eedf54504110c1e88c5233b0b922667b00f87751452164cc00"},{"executable":false,"path":"src/core/chatgpt-desktop/errors.ts","sha256":"941ce4a9148fc53086cce64336dba473c1f7e4138774fd50a8ae524dcc05d34d"},{"executable":false,"path":"src/core/chatgpt-desktop/facade.ts","sha256":"bc9ac3bfd24db8458af6d20893fb3ab2ed701778f709aadbd141d61ed6c6c29b"},{"executable":false,"path":"src/core/chatgpt-desktop/index.ts","sha256":"591e6968c008cc75827da94b91c0ea15e573640876814e35e2470342dc8bccf9"},{"executable":false,"path":"src/core/chatgpt-desktop/loopback.ts","sha256":"b746681e5c45c67445e6620f850eff7c4627e13979747a7f686374c10a51f52f"},{"executable":false,"path":"src/core/chatgpt-desktop/payload-budget.ts","sha256":"89410474315b3769bad58d3ab61999e169410b7d1fdc20819efb4ee9c82fe1ac"},{"executable":false,"path":"src/core/chatgpt-desktop/project-state.ts","sha256":"b33e55b6d6abf15f3d7d1489b8b178d4e907ea83ed1a47d88f8f178a574709f8"},{"executable":false,"path":"src/core/chatgpt-desktop/relaunch.ts","sha256":"14851e115de7c45e256c4b9c3829dc4e796f1d989cf67d65115701a30b6d3a81"},{"executable":false,"path":"src/core/chatgpt-desktop/remote-threads.ts","sha256":"ed93418ff5d2bb51016783e7b0a458805e709b212974a66be804ddb9f7038033"},{"executable":false,"path":"src/core/chatgpt-desktop/routes.ts","sha256":"ccf8cde780da6e6e03f11e4dcc0d9d6bc4a6a3fd20d3210e74f0217e3660b7e1"},{"executable":false,"path":"src/core/chatgpt-desktop/thread-ids.ts","sha256":"0fe25e9d32c078bd9e7a171ab4bc4ee0b15190cd6d6c4d3b345926b3c608e082"},{"executable":false,"path":"src/core/chatgpt-desktop/types.ts","sha256":"59f89694eed34206e32db2a0afb48670bb164a4ffffacb907c8629bb0ce4bcca"},{"executable":false,"path":"src/core/claude-channel.js","sha256":"01ac7daef748a11bc99e132950ed7c59f50442e417eecf894b59cb4bb484202f"},{"executable":false,"path":"src/core/claude-routes.ts","sha256":"d34ddf25b8634ef86d46ab663f30a49aaedf87f8dc98e4036f84c1c88efa4083"},{"executable":false,"path":"src/core/codex-bridge.js","sha256":"cce4df66b5c8207ca5ac1aac1f04a07146ee8aa574063206736a4149de7cdc3a"},{"executable":false,"path":"src/core/codex/contract.js","sha256":"16bcbd0fc09844166c52694ebb1075e60b91296a8f04fb02ad061c30b0e9c591"},{"executable":false,"path":"src/core/codex/conversation.js","sha256":"05abb2253480d0793bf5f6b3db17c47e7d154973747074c7c76a79e6f61b7deb"},{"executable":false,"path":"src/core/codex/remote-control.js","sha256":"52565007799e98c3bff4297287d8e53005e285a95bab9e4e4d0a088bded4fa8e"},{"executable":false,"path":"src/core/codex/routes.ts","sha256":"4fd73b060ee7bc6ac00abe552b1e39baca5e9aba0bb1a095267148c00d0526cc"},{"executable":false,"path":"src/core/codex/thread-id.js","sha256":"4eb6c1929c2e7687ed6c1422789fa01c3e1c556ead7d16355eafbae46a7126f4"},{"executable":false,"path":"src/core/commands.js","sha256":"3ad8fafb86744a57992da93ad838ade0e9acb1b7ca09b829cefadbf2781be0c9"},{"executable":false,"path":"src/core/desktop-shim-bridge.js","sha256":"58f3f39b8527adf58f57313e02e34322b203b8c38d1acfbad419a8b9af214443"},{"executable":false,"path":"src/core/desktop-shim.js","sha256":"f2f78bf3a336c9a4924886222c8ffae6d4d002125f7f2e09ffc6f9d1a2dbb4e2"},{"executable":false,"path":"src/core/format.js","sha256":"27e02ed2a71ccf51d4d088368865b4d8686bc2c22aae3cdc2b1ad91de51224f3"},{"executable":false,"path":"src/core/gateway.js","sha256":"3a4f838ee423600d29e05f58060dcaf81cdcea4ac93b36dd6108363ac24bc606"},{"executable":false,"path":"src/core/grok-approval-routes.ts","sha256":"a33daa4d05e0c2594c795bd7654c22f3e1003e4aaad41cd70034d03651dd01f9"},{"executable":false,"path":"src/core/grok-approvals.js","sha256":"6b651a65bdd17241dd5b7a01d94a318ece9356c1c9b965b0578e08250d74e8fe"},{"executable":false,"path":"src/core/headers.js","sha256":"e229f2826ebdfb52949dab500798c0fa8a5ff7c4e63de91a63103bf45d2ae3cc"},{"executable":false,"path":"src/core/history.js","sha256":"38ca8e8f8dcd2ad42952c424ea9f00be5972d8751e4c0ac87214171903d82d89"},{"executable":false,"path":"src/core/relay/codex.js","sha256":"ff6e8f3158d171911a35b829cd76e1f5fd8319f8143f727a4854448fc09edeec"},{"executable":false,"path":"src/core/relay/completion.js","sha256":"1a4bb1b434a9b4bf150ac27e32d0de8fa9d34df61639c55fa9f0f667d72176c5"},{"executable":false,"path":"src/core/relay/control.js","sha256":"3645540f9f799b8742e0d5782b6324454a1486a780f6cb97260b96ef62411771"},{"executable":false,"path":"src/core/relay/detail.js","sha256":"59a5faae60c762a8428b369d6f5574874130c26eb8c4a0a0ac0941a330620730"},{"executable":false,"path":"src/core/relay/engine.js","sha256":"bed4fab0e3791324d73e1594884e06427969816c86f3423dcb5e7a0a9a4a4c24"},{"executable":false,"path":"src/core/relay/intake.js","sha256":"b31146fe3c9edd2f09323a454687403666591b6c12483a55b07ac610d2e3610a"},{"executable":false,"path":"src/core/relay/interactions.js","sha256":"6b576ed3a5b6659bc17e384d26e40d6ccb41b118b3a91cf5c902e9758ccbc1b1"},{"executable":false,"path":"src/core/relay/managed.js","sha256":"dbe03ed964bd09739e580de73b7e9f90508062fa2e53b8244c6ca7f78fc7a60d"},{"executable":false,"path":"src/core/relay/ownership.js","sha256":"74805f04f6a8bc02b2951614c4814aa42f54aaa3abca027a2413ed5a54814caf"},{"executable":false,"path":"src/core/relay/profile.js","sha256":"3347d1d493d3470d82722468d31571464d8e11aa0b4deac5bda75f574078dfe5"},{"executable":false,"path":"src/core/relay/records.js","sha256":"d0378385eff9dbc4a732fa08998681d0a47783765d99232d70ffe6e100fb6421"},{"executable":false,"path":"src/core/relay/routes.ts","sha256":"c749c0e722002c6484ebb349fab07d4361322d5a57adf8d2f9e58a1ae1dd4c65"},{"executable":false,"path":"src/core/relay/state.js","sha256":"3f18c310da9bf121e3fc238e75540e337b6b7406817a18c662d8af1f697c5ce7"},{"executable":false,"path":"src/core/relay/worker.js","sha256":"4176ad91872f36f7ea6565d9f30d23d9d7a80ef27d7af975af760457132ba148"},{"executable":false,"path":"src/core/sender-prefix.js","sha256":"8c7f8f665b8f3a8739071e7a64cb248069ddcc9a01c1aedc9c64d1cd6cc14550"},{"executable":false,"path":"src/core/store.js","sha256":"caefeeebf686cecd37ef19585ef0a5d3acba9dbf1564fe128c3afe4de067305c"},{"executable":false,"path":"src/core/transcript.js","sha256":"0355f0978d2d126b90858e3f661130243fc4730ee81ba13080af1dfa39d8b081"},{"executable":false,"path":"src/core/url-policy.js","sha256":"62bf6d3c8af1f5840d5e2d682d8f2e52fcbfbee6695728b47ed31816f1736089"},{"executable":false,"path":"src/core/user-machine-guidance.js","sha256":"c8409ae9679246d8f907f1e0d16ec1c415d6697f2ce048fb64a599734486b9a6"},{"executable":false,"path":"src/gbot-install.ts","sha256":"e3c833d7662610e70e65232e541017229fb9a049eacee0bdf31e2c33c6ab7189"},{"executable":false,"path":"src/gbot.ts","sha256":"c89a37b764956ceacb2917a991a6d82d987ed8d8e453c8d553485bbd7f3edcc0"},{"executable":false,"path":"src/mcp/claude-channel.ts","sha256":"2d52e0d862a492026b6cb408e3177c994996e2b4292903da5bbf5bc191019913"},{"executable":false,"path":"src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx","sha256":"86334421686350ea044fb1160c19b6f5889425feb78fd608675feb8137492090"},{"executable":false,"path":"src/mcp/grok-bot/tools/chatgpt_desktop_list_threads.tsx","sha256":"5f24cfdace92522891dfbaed0bd90e74b7de6bb8d193e0ef3bd5582c05a06914"},{"executable":false,"path":"src/mcp/grok-bot/tools/chatgpt_desktop_open_thread.tsx","sha256":"b415d5bd303693649abae96b6b9d2920d29c797f5367ae8fbb2b62b635528e37"},{"executable":false,"path":"src/mcp/grok-bot/tools/chatgpt_desktop_read_thread.tsx","sha256":"6adc4318f4b6c90b1d3970046266197348f79757c0d52ffe991d6338eb1fed6c"},{"executable":false,"path":"src/mcp/grok-bot/tools/chatgpt_desktop_search_threads.tsx","sha256":"1d50db88cf7d06a74524c8937ed9045eaaf2560f6766d6c411b3bb55575d1d3f"},{"executable":false,"path":"src/mcp/grok-bot/tools/chatgpt_desktop_send.tsx","sha256":"9e1b93735b9a38216c7e9aac4c5b62bc2ac20b60e1979a19c4610f0208961af5"},{"executable":false,"path":"src/mcp/grok-bot/tools/chatgpt_desktop_status.tsx","sha256":"681e50ae6c7d3a04a76ad8eadf865ab9d793b5eb3c78f961a42d0902adacc306"},{"executable":false,"path":"src/mcp/grok-bot/tools/chatgpt_desktop_wait_reply.tsx","sha256":"2786b62ff2a1a4e68229199935ac63c15614a0f0bdbef74b2d06e0c405516272"},{"executable":false,"path":"src/mcp/grok-bot/tools/claude_send.tsx","sha256":"c02924049f370d9cb5ae459d6275c6cae2c9f4f1328cb5fa8e2f3e6d12a6d656"},{"executable":false,"path":"src/mcp/grok-bot/tools/codex_new.tsx","sha256":"556c8d6f5f2b07b20e9daae7aa8008ee3775582a4d034415fc86fecda0c63866"},{"executable":false,"path":"src/mcp/grok-bot/tools/codex_send.tsx","sha256":"e1c972255f4efd84d55e8163017e5675dd4f6315eef0b503ba5105d59142026b"},{"executable":false,"path":"src/mcp/grok-bot/tools/codex_threads.tsx","sha256":"c35cae75d1a46d098173cab6f1e401b51d695f40d4bc307afae9dd4a62a6bd1a"},{"executable":false,"path":"src/mcp/grok-bot/tools/codex_wait.tsx","sha256":"cb08b094860f3a09c937eb8339108015fe0de57d9f6c6886aea2028cd33ef207"},{"executable":false,"path":"src/mcp/grok-bot/tools/codex_watch.tsx","sha256":"1bc603d36ea7542468e7e3eadbb86072990282e09e209f13ebfc64389d207c49"},{"executable":false,"path":"src/mcp/grok-bot/tools/gbot_bridge_start.tsx","sha256":"c75c5c8a30ec43709a4d32baa8c3bc9796595381bb04fd2499ca30cd515c5a0b"},{"executable":false,"path":"src/mcp/grok-bot/tools/gbot_bridge_status.tsx","sha256":"486ce643b35cfa2eabf78f0deeabce7198adc66133669eec630e1d12206ad642"},{"executable":false,"path":"src/mcp/grok-bot/tools/gbot_bridge_stop.tsx","sha256":"371428a6e4b4e1312dd95d9681c29e765aec55e65d80d7c474535df838a43457"},{"executable":false,"path":"src/mcp/grok-bot/tools/gbot_codex_respond.tsx","sha256":"030e9c2081ed4ed40732b098c929022af3083f609f10934b35e1f428d33ae637"},{"executable":false,"path":"src/mcp/grok-bot/tools/gbot_grok_approvals.tsx","sha256":"17f164acdc48d6bc79a4c5044c7082760dd2a1646e5b56f6bff54c53fed96451"},{"executable":false,"path":"src/mcp/grok-bot/tools/gbot_grok_respond.tsx","sha256":"41964bb8cf0eb0f8493afa1dde16cd94c4f589d1a7e410577d1ed63ec4648542"},{"executable":false,"path":"src/mcp/grok-bot/tools/gbot_send.tsx","sha256":"2101d279a0e6bda2d57ba3da5366411df460a95692d4e64a1096880a89121d9a"},{"executable":false,"path":"src/mcp/grok-bot/tools/gbot_thread.tsx","sha256":"ef4aefec7243efaad18335e8508c7c17af0cd9b0f8a4b3693a7af9f666807fd6"},{"executable":false,"path":"src/rules/codex-claude-on-user-machines.mdc","sha256":"a50ec93d021a807e0a18bf9311cdf103bc0ae3947e4c6a0bd78cbb941e8e5b24"},{"executable":false,"path":"src/scripts/gbot-relay.ts","sha256":"6690e316189ed8ebba43ea67a495338c927e4c0c21a9f896b7b2fecc8c6eb656"},{"executable":false,"path":"src/skills/chatgpt-desktop/SKILL.md","sha256":"0fe2560037f761203a317bf8755b0476ce864b7b1b13708559448d300aa4635c"},{"executable":false,"path":"src/skills/codex-dot/SKILL.md","sha256":"7e910c3e9e74800c09c9643983cf51cd090ff6ba448800d2702968cd89c6a005"},{"executable":false,"path":"src/skills/talk-to-grok-bot/references/bridge-administration.md","sha256":"b43f91464cdb885831bbf945766e4d9644fea017049ebcde3241a2da5ff0fc81"},{"executable":false,"path":"src/skills/talk-to-grok-bot/SKILL.md","sha256":"33d526cf5cd5d458d5f5b80992f201918077fd0ca8c74b8e15fbe2dd78d435bb"},{"executable":false,"path":"test/app-session.test.js","sha256":"0756b691703dd8c84bf0823f2afab16c6b158398e4ce6be229db6f459a0bbdb1"},{"executable":false,"path":"test/claude-channel.test.js","sha256":"c63337698001b9739414203d70b6d54d0794ec5970bd90d327923f50ab2355dc"},{"executable":false,"path":"test/codex-bridge.test.js","sha256":"f885418d4d8aa6337202c839539cfa013a9c4471263bc9af2631883febc4a84b"},{"executable":false,"path":"test/codex-commands.test.js","sha256":"d07355741037270a82f9319ef615ace388585c44cff5586868330b6811847d2c"},{"executable":false,"path":"test/codex-contract.test.js","sha256":"926c7609679d8b6ac3b568d56ba5efa20df86d016a9031f085d19adbfb0e9064"},{"executable":false,"path":"test/codex-conversation.test.js","sha256":"789d0245a8d470860f7535c6ef3849313205097f6df517ab9946e47a789f8534"},{"executable":false,"path":"test/codex-session.test.js","sha256":"b39fe6afe0784b16e37b208089279dea01095d33e06ae26a1b60871796c7738a"},{"executable":false,"path":"test/codex-surfaces.test.js","sha256":"56d1b6550cc69d462a05e892cf82aa6a41495857b753215104e03207db514c81"},{"executable":false,"path":"test/connect-gateway.test.js","sha256":"5fabd87640b39e75c218300ca6be278268f248f3ff75e8cb72f9fade9eb0f7f3"},{"executable":false,"path":"test/desktop-shim.test.js","sha256":"89fb6c38a5fca5d41a96434cf9e8d51ae1d9992cf4f7930df0cc5840ef985705"},{"executable":false,"path":"test/doctor.test.js","sha256":"9685885df9e7363d3e077d246b1a17803c8899e9613b4a4d52ee4b9275e20694"},{"executable":false,"path":"test/gateway-groups.test.js","sha256":"14e227453e3e28c7cb60f91dba55733834c023ce9a914e40e22c68c899512bdc"},{"executable":false,"path":"test/gateway-send.test.js","sha256":"d059166153dde975f457e4e8c32ea08b0fc857b58fd48eab7da1959638e4fb05"},{"executable":false,"path":"test/gateway-skills.test.js","sha256":"feed62a884d1b7d12e358a27d73fb5587bcf761d4867ab1755d8bc34a4380069"},{"executable":false,"path":"test/gateway.test.js","sha256":"e5a7acd7ae347c82ef1e2a6000562767168e56d6f9d86aca8da46141731d1309"},{"executable":false,"path":"test/grok-approvals.test.js","sha256":"5ad4c548060a08dbe57949ab5ae240383fdb1709cd3694a909f5e1354fee3f73"},{"executable":false,"path":"test/helpers/codex-server.js","sha256":"432b4570601e94cf08c4861133d328404a97066642edb7ec0dbc314411a4d848"},{"executable":false,"path":"test/history.test.js","sha256":"eac4811f1b12d427df66996f33dd46bc3cbe31fc9fa030b3f32c571d5379f8dd"},{"executable":false,"path":"test/host-tool-inventory.test.js","sha256":"fa44f15f46d0cae486c13be3fae09568f17f8581001ec50a61478bec03f4c28c"},{"executable":false,"path":"test/pr69-integration.test.js","sha256":"a5dc99c11e2d3fcf18233c595f9c28301e5939f4b3657125eb3f970df2598840"},{"executable":false,"path":"test/relay-auth-recovery.test.js","sha256":"399c064f550fe33e166187a51da527728a97946d213377c8fcc8231378f5bbab"},{"executable":false,"path":"test/relay-codex.test.js","sha256":"43cb313e748e187f798a8a0bce5a01cce9ba360a52985b05ecededee9ad10581"},{"executable":false,"path":"test/relay-completion.test.js","sha256":"a8cc2402f82ce4b458074eafd32c8fa18f7ced7d85fdb6b7fa68f7b18b6a8c2e"},{"executable":false,"path":"test/relay-engine.test.js","sha256":"f02d137b0ef953dc46a9c6367742f455d58e31b7e3432130ddcf2844c9684f16"},{"executable":false,"path":"test/relay-failure-detail.test.js","sha256":"4b514b25e8938d72ee13b29e56b54d4d42b0058cc5022065eb37f5a5c1063d5d"},{"executable":false,"path":"test/relay-gateway-lifecycle.test.js","sha256":"e16e7a34f7b6bb07965003e3018aa3a43ff192602db1c177bec9b6268fddb78b"},{"executable":false,"path":"test/relay-interactions.test.js","sha256":"83d70b1a813923fc6bae02b18d328107d0d04a6a761ab73b2051e3d114c7eaa5"},{"executable":false,"path":"test/relay-lifecycle.test.js","sha256":"e90130e1f609839bfd3f74c3edc333a22f37fe20dcde0146daa8d497133da4b7"},{"executable":false,"path":"test/relay-state.test.js","sha256":"c9143d5fa150b1ebcef9d515e161e25af3370b71eaf800a7b2bb1da1678b0bd3"},{"executable":false,"path":"test/relay-surfaces.test.js","sha256":"ba17ac4f1ffc02d4e06cd73eda143422a3d4d2f750630af9ed4e828dc9d7d2be"},{"executable":false,"path":"test/relay-worker.test.js","sha256":"6bd1bdaaaae59997132f289f822b6c4b8c9616c268fb2b45861e03f697c08197"},{"executable":false,"path":"test/release-config.test.js","sha256":"f3078277086d722f21d07e4134e10b89d2e849a94a0fa40f30025a5cb6fa6a15"},{"executable":false,"path":"test/store.test.js","sha256":"5d4ae4295287539ef4b7f98d0ca0cd42a931a3c7b25b092fbd2b8f1a08ca4bac"},{"executable":false,"path":"test/test-runner.test.js","sha256":"966ff3b67edb8e1f03b50120d4d820ab0f871121e9b359609e73faae8446454f"},{"executable":false,"path":"test/transcript.test.js","sha256":"28795d9cc8e42653ac430d18e17ff1e6bc2feb1b7935fe5c69b336d58e1793ae"},{"executable":false,"path":"test/url-policy.test.js","sha256":"dacfe322e0cbff178c0d67a5f76b6bd8d9fef14aa57c09c697eb69fa0019629c"},{"executable":false,"path":"tests/fixtures/codex-global-state-remote.json","sha256":"e59377d0f03cb24d57b55558e9c91dd363619a8a92566835f1ad49b639f99525"},{"executable":false,"path":"tests/route-unit/chatgpt-desktop-e2e-regressions.test.ts","sha256":"9cfc7f538899954970fe643c13571f03d417450c519525e91d382a0b5f0b086a"},{"executable":false,"path":"tests/route-unit/chatgpt-desktop-mcp-followup.test.ts","sha256":"4a2db11a642b8daef05285f096ce026ca5b9b6394c31c0966d7e5f8c1d49f4ca"},{"executable":false,"path":"tests/route-unit/chatgpt-desktop-review.test.ts","sha256":"208dfdea18ccc408a279666a521d2f70fdce9bdaf28f86bc5d053f7ad09a7dd2"},{"executable":false,"path":"tests/route-unit/chatgpt-desktop.test.ts","sha256":"5cf61e174b1d4a7c0f2209d6746bb1880a96fe550f7d2c4ea94e849dd7f21299"},{"executable":false,"path":"tests/route-unit/tools.test.ts","sha256":"aa3dc253ac54fbd2da06cdd29b0a5163079a045d4c7302233149781009137cd7"},{"executable":false,"path":"tsconfig.json","sha256":"fa2ced6d1721e8280a6aed615717cffcef25f6e2d16e15889b2be2a9df4fc226"}]},"provenance":[{"path":".agents/plugins/marketplace.json","sourceInputs":["agent-bundle.config.ts"]},{"path":".claude-plugin/marketplace.json","sourceInputs":["agent-bundle.config.ts","package.json"]},{"path":".claude-plugin/plugin.json","sourceInputs":["agent-bundle.config.ts","src/mcp/claude-channel.ts","src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx","src/skills/chatgpt-desktop/SKILL.md","src/skills/codex-dot/SKILL.md","src/skills/talk-to-grok-bot/SKILL.md"]},{"path":".codex-plugin/mcp.json","sourceInputs":["agent-bundle.config.ts","src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx"]},{"path":".codex-plugin/plugin.json","sourceInputs":["agent-bundle.config.ts","package.json","src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx","src/skills/chatgpt-desktop/SKILL.md","src/skills/codex-dot/SKILL.md","src/skills/talk-to-grok-bot/SKILL.md"]},{"path":".cursor-plugin/marketplace.json","sourceInputs":["agent-bundle.config.ts"]},{"path":".cursor-plugin/mcp.json","sourceInputs":["agent-bundle.config.ts","src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx"]},{"path":".cursor-plugin/plugin.json","sourceInputs":["agent-bundle.config.ts","package.json","src/commands/codex-send.md","src/commands/codex-threads.md","src/commands/codex-wait.md","src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx","src/rules/codex-claude-on-user-machines.mdc","src/skills/chatgpt-desktop/SKILL.md","src/skills/codex-dot/SKILL.md","src/skills/talk-to-grok-bot/SKILL.md"]},{"path":".mcp.json","sourceInputs":["agent-bundle.config.ts","src/mcp/claude-channel.ts","src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx"]},{"path":"agent-bundle.compile-evidence.json","sourceInputs":["agent-bundle.config.ts"]},{"path":"bin/gbot-flight.mjs","sourceInputs":["agent-bundle.config.ts","package.json","src/cli/_shared.ts","src/cli/approvals/list.tsx","src/cli/approvals/respond.tsx","src/cli/bots/create.tsx","src/cli/bots/delete.tsx","src/cli/bots/get.tsx","src/cli/bots/list.tsx","src/cli/bots/update.tsx","src/cli/chatgpt-desktop/hosts.tsx","src/cli/chatgpt-desktop/read.tsx","src/cli/chatgpt-desktop/search.tsx","src/cli/chatgpt-desktop/send.tsx","src/cli/chatgpt-desktop/status.tsx","src/cli/chatgpt-desktop/threads.tsx","src/cli/claude/send.tsx","src/cli/codex/bridge/respond.tsx","src/cli/codex/bridge/run.tsx","src/cli/codex/bridge/start.tsx","src/cli/codex/bridge/status.tsx","src/cli/codex/bridge/stop.tsx","src/cli/codex/desktop-shim.tsx","src/cli/codex/list-threads.tsx","src/cli/codex/new.tsx","src/cli/codex/queue.tsx","src/cli/codex/send.tsx","src/cli/codex/status.tsx","src/cli/codex/wait.tsx","src/cli/codex/watch.tsx","src/cli/doctor.tsx","src/cli/groups/add.tsx","src/cli/groups/create.tsx","src/cli/groups/delete.tsx","src/cli/groups/get.tsx","src/cli/groups/list.tsx","src/cli/groups/members.tsx","src/cli/groups/remove.tsx","src/cli/groups/set.tsx","src/cli/groups/update.tsx","src/cli/history.tsx","src/cli/send.tsx","src/cli/skills/add.tsx","src/cli/skills/list.tsx","src/cli/skills/remove.tsx","src/cli/thread.tsx","src/core/app-session.js","src/core/chatgpt-desktop/app-server-fallback.ts","src/core/chatgpt-desktop/cdp-adapter.ts","src/core/chatgpt-desktop/cdp-dom.ts","src/core/chatgpt-desktop/cdp-session.ts","src/core/chatgpt-desktop/errors.ts","src/core/chatgpt-desktop/facade.ts","src/core/chatgpt-desktop/loopback.ts","src/core/chatgpt-desktop/payload-budget.ts","src/core/chatgpt-desktop/project-state.ts","src/core/chatgpt-desktop/remote-threads.ts","src/core/chatgpt-desktop/routes.ts","src/core/chatgpt-desktop/thread-ids.ts","src/core/claude-channel.js","src/core/claude-routes.ts","src/core/codex-bridge.js","src/core/codex/contract.js","src/core/codex/conversation.js","src/core/codex/remote-control.js","src/core/codex/routes.ts","src/core/codex/thread-id.js","src/core/commands.js","src/core/desktop-shim-bridge.js","src/core/desktop-shim.js","src/core/format.js","src/core/gateway.js","src/core/grok-approval-routes.ts","src/core/grok-approvals.js","src/core/headers.js","src/core/history.js","src/core/relay/control.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/relay/routes.ts","src/core/sender-prefix.js","src/core/store.js","src/core/transcript.js","src/core/url-policy.js","src/core/user-machine-guidance.js","src/gbot.ts"]},{"path":"bin/gbot.mjs","sourceInputs":["agent-bundle.config.ts","package.json","src/cli/_shared.ts","src/cli/approvals/list.tsx","src/cli/approvals/respond.tsx","src/cli/bots/create.tsx","src/cli/bots/delete.tsx","src/cli/bots/get.tsx","src/cli/bots/list.tsx","src/cli/bots/update.tsx","src/cli/chatgpt-desktop/hosts.tsx","src/cli/chatgpt-desktop/read.tsx","src/cli/chatgpt-desktop/search.tsx","src/cli/chatgpt-desktop/send.tsx","src/cli/chatgpt-desktop/status.tsx","src/cli/chatgpt-desktop/threads.tsx","src/cli/claude/send.tsx","src/cli/codex/bridge/respond.tsx","src/cli/codex/bridge/run.tsx","src/cli/codex/bridge/start.tsx","src/cli/codex/bridge/status.tsx","src/cli/codex/bridge/stop.tsx","src/cli/codex/desktop-shim.tsx","src/cli/codex/list-threads.tsx","src/cli/codex/new.tsx","src/cli/codex/queue.tsx","src/cli/codex/send.tsx","src/cli/codex/status.tsx","src/cli/codex/wait.tsx","src/cli/codex/watch.tsx","src/cli/doctor.tsx","src/cli/groups/add.tsx","src/cli/groups/create.tsx","src/cli/groups/delete.tsx","src/cli/groups/get.tsx","src/cli/groups/list.tsx","src/cli/groups/members.tsx","src/cli/groups/remove.tsx","src/cli/groups/set.tsx","src/cli/groups/update.tsx","src/cli/history.tsx","src/cli/send.tsx","src/cli/skills/add.tsx","src/cli/skills/list.tsx","src/cli/skills/remove.tsx","src/cli/thread.tsx","src/core/app-session.js","src/core/chatgpt-desktop/app-server-fallback.ts","src/core/chatgpt-desktop/cdp-adapter.ts","src/core/chatgpt-desktop/cdp-dom.ts","src/core/chatgpt-desktop/cdp-session.ts","src/core/chatgpt-desktop/errors.ts","src/core/chatgpt-desktop/facade.ts","src/core/chatgpt-desktop/loopback.ts","src/core/chatgpt-desktop/payload-budget.ts","src/core/chatgpt-desktop/project-state.ts","src/core/chatgpt-desktop/remote-threads.ts","src/core/chatgpt-desktop/routes.ts","src/core/chatgpt-desktop/thread-ids.ts","src/core/claude-channel.js","src/core/claude-routes.ts","src/core/codex-bridge.js","src/core/codex/contract.js","src/core/codex/conversation.js","src/core/codex/remote-control.js","src/core/codex/routes.ts","src/core/codex/thread-id.js","src/core/commands.js","src/core/desktop-shim-bridge.js","src/core/desktop-shim.js","src/core/format.js","src/core/gateway.js","src/core/grok-approval-routes.ts","src/core/grok-approvals.js","src/core/headers.js","src/core/history.js","src/core/relay/control.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/relay/routes.ts","src/core/sender-prefix.js","src/core/store.js","src/core/transcript.js","src/core/url-policy.js","src/core/user-machine-guidance.js","src/gbot.ts"]},{"path":"commands/codex-send.md","sourceInputs":["src/commands/codex-send.md"]},{"path":"commands/codex-threads.md","sourceInputs":["src/commands/codex-threads.md"]},{"path":"commands/codex-wait.md","sourceInputs":["src/commands/codex-wait.md"]},{"path":"INSTALL.md","sourceInputs":["agent-bundle.config.ts"]},{"path":"install.mjs","sourceInputs":["agent-bundle.config.ts"]},{"path":"mcp.json","sourceInputs":["src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx"]},{"path":"mcp/mcp-claude-channel-8029413c.mjs","sourceInputs":["src/core/claude-channel.js","src/core/user-machine-guidance.js","src/mcp/claude-channel.ts"]},{"path":"mcp/mcp-grok-bot-b8c2461e-flight.mjs","sourceInputs":["package.json","src/core/app-session.js","src/core/chatgpt-desktop/app-server-fallback.ts","src/core/chatgpt-desktop/cdp-adapter.ts","src/core/chatgpt-desktop/cdp-dom.ts","src/core/chatgpt-desktop/cdp-session.ts","src/core/chatgpt-desktop/errors.ts","src/core/chatgpt-desktop/facade.ts","src/core/chatgpt-desktop/loopback.ts","src/core/chatgpt-desktop/payload-budget.ts","src/core/chatgpt-desktop/project-state.ts","src/core/chatgpt-desktop/remote-threads.ts","src/core/chatgpt-desktop/routes.ts","src/core/chatgpt-desktop/thread-ids.ts","src/core/claude-channel.js","src/core/claude-routes.ts","src/core/codex-bridge.js","src/core/codex/contract.js","src/core/codex/conversation.js","src/core/codex/remote-control.js","src/core/codex/routes.ts","src/core/codex/thread-id.js","src/core/desktop-shim-bridge.js","src/core/desktop-shim.js","src/core/format.js","src/core/gateway.js","src/core/grok-approval-routes.ts","src/core/grok-approvals.js","src/core/headers.js","src/core/relay/control.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/relay/routes.ts","src/core/sender-prefix.js","src/core/store.js","src/core/transcript.js","src/core/url-policy.js","src/core/user-machine-guidance.js","src/gbot.ts","src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_list_threads.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_open_thread.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_read_thread.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_search_threads.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_send.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_status.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_wait_reply.tsx","src/mcp/grok-bot/tools/claude_send.tsx","src/mcp/grok-bot/tools/codex_new.tsx","src/mcp/grok-bot/tools/codex_send.tsx","src/mcp/grok-bot/tools/codex_threads.tsx","src/mcp/grok-bot/tools/codex_wait.tsx","src/mcp/grok-bot/tools/codex_watch.tsx","src/mcp/grok-bot/tools/gbot_bridge_start.tsx","src/mcp/grok-bot/tools/gbot_bridge_status.tsx","src/mcp/grok-bot/tools/gbot_bridge_stop.tsx","src/mcp/grok-bot/tools/gbot_codex_respond.tsx","src/mcp/grok-bot/tools/gbot_grok_approvals.tsx","src/mcp/grok-bot/tools/gbot_grok_respond.tsx","src/mcp/grok-bot/tools/gbot_send.tsx","src/mcp/grok-bot/tools/gbot_thread.tsx"]},{"path":"mcp/mcp-grok-bot-b8c2461e.mjs","sourceInputs":["package.json","src/core/app-session.js","src/core/chatgpt-desktop/app-server-fallback.ts","src/core/chatgpt-desktop/cdp-adapter.ts","src/core/chatgpt-desktop/cdp-dom.ts","src/core/chatgpt-desktop/cdp-session.ts","src/core/chatgpt-desktop/errors.ts","src/core/chatgpt-desktop/facade.ts","src/core/chatgpt-desktop/loopback.ts","src/core/chatgpt-desktop/payload-budget.ts","src/core/chatgpt-desktop/project-state.ts","src/core/chatgpt-desktop/remote-threads.ts","src/core/chatgpt-desktop/routes.ts","src/core/chatgpt-desktop/thread-ids.ts","src/core/claude-channel.js","src/core/claude-routes.ts","src/core/codex-bridge.js","src/core/codex/contract.js","src/core/codex/conversation.js","src/core/codex/remote-control.js","src/core/codex/routes.ts","src/core/codex/thread-id.js","src/core/desktop-shim-bridge.js","src/core/desktop-shim.js","src/core/format.js","src/core/gateway.js","src/core/grok-approval-routes.ts","src/core/grok-approvals.js","src/core/headers.js","src/core/relay/control.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/relay/routes.ts","src/core/sender-prefix.js","src/core/store.js","src/core/transcript.js","src/core/url-policy.js","src/core/user-machine-guidance.js","src/gbot.ts","src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_list_threads.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_open_thread.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_read_thread.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_search_threads.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_send.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_status.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_wait_reply.tsx","src/mcp/grok-bot/tools/claude_send.tsx","src/mcp/grok-bot/tools/codex_new.tsx","src/mcp/grok-bot/tools/codex_send.tsx","src/mcp/grok-bot/tools/codex_threads.tsx","src/mcp/grok-bot/tools/codex_wait.tsx","src/mcp/grok-bot/tools/codex_watch.tsx","src/mcp/grok-bot/tools/gbot_bridge_start.tsx","src/mcp/grok-bot/tools/gbot_bridge_status.tsx","src/mcp/grok-bot/tools/gbot_bridge_stop.tsx","src/mcp/grok-bot/tools/gbot_codex_respond.tsx","src/mcp/grok-bot/tools/gbot_grok_approvals.tsx","src/mcp/grok-bot/tools/gbot_grok_respond.tsx","src/mcp/grok-bot/tools/gbot_send.tsx","src/mcp/grok-bot/tools/gbot_thread.tsx"]},{"path":"plugin.json","sourceInputs":["agent-bundle.config.ts","package.json"]},{"path":"rules/codex-claude-on-user-machines.mdc","sourceInputs":["src/rules/codex-claude-on-user-machines.mdc"]},{"path":"scripts/gbot-relay.mjs","sourceInputs":["package.json","src/core/app-session.js","src/core/codex-bridge.js","src/core/codex/contract.js","src/core/codex/conversation.js","src/core/codex/remote-control.js","src/core/codex/thread-id.js","src/core/desktop-shim-bridge.js","src/core/desktop-shim.js","src/core/gateway.js","src/core/grok-approvals.js","src/core/headers.js","src/core/relay/codex.js","src/core/relay/completion.js","src/core/relay/control.js","src/core/relay/detail.js","src/core/relay/engine.js","src/core/relay/intake.js","src/core/relay/interactions.js","src/core/relay/ownership.js","src/core/relay/profile.js","src/core/relay/records.js","src/core/relay/state.js","src/core/relay/worker.js","src/core/sender-prefix.js","src/core/store.js","src/core/transcript.js","src/core/url-policy.js","src/core/user-machine-guidance.js","src/scripts/gbot-relay.ts"]},{"path":"skills/chatgpt-desktop/SKILL.md","sourceInputs":["src/skills/chatgpt-desktop/SKILL.md"]},{"path":"skills/codex-dot/SKILL.md","sourceInputs":["src/skills/codex-dot/SKILL.md"]},{"path":"skills/talk-to-grok-bot/references/bridge-administration.md","sourceInputs":["src/skills/talk-to-grok-bot/references/bridge-administration.md","src/skills/talk-to-grok-bot/SKILL.md"]},{"path":"skills/talk-to-grok-bot/SKILL.md","sourceInputs":["src/skills/talk-to-grok-bot/SKILL.md"]}],"recordVersion":1,"validation":{"artifact":{"status":"passed"},"projections":[{"host":"claude","status":"passed"},{"host":"codex","status":"passed"},{"host":"cursor","status":"passed"},{"host":"portable","status":"passed"}],"source":{"status":"passed"}}},"distribution":{"channels":["local","npm"],"install":{"instructions":"INSTALL.md","script":"install.mjs"},"payloads":[]},"executables":{"bins":[{"hosts":["claude","codex","cursor","portable"],"name":"gbot","path":"bin/gbot.mjs","worker":"bin/gbot-flight.mjs"}],"hooks":[],"mcpServers":[{"apps":[],"hosts":["claude"],"id":"mcp:claude-channel","kind":"compiled","launch":{"args":[],"entry":"mcp/mcp-claude-channel-8029413c.mjs","env":{}},"name":"claude-channel","transport":"stdio"},{"apps":[],"hosts":["claude","codex","cursor","portable"],"id":"mcp:grok-bot","kind":"compiled","launch":{"args":[],"entry":"mcp/mcp-grok-bot-b8c2461e.mjs","env":{},"worker":"mcp/mcp-grok-bot-b8c2461e-flight.mjs"},"name":"grok-bot","transport":"stdio"}],"scripts":[{"hosts":["claude","codex","cursor","portable"],"id":"script:gbot-relay","mode":"bundle","name":"gbot-relay","path":"scripts/gbot-relay.mjs"}]},"files":[{"bytes":225,"kind":"generated","path":".agents/plugins/marketplace.json","sha256":"54d575719e003eb71ba2d7fb7379e12098243e701646e0ca83dba38ef2a78b1d"},{"bytes":926,"kind":"generated","path":".claude-plugin/marketplace.json","sha256":"7335705d34b85380cfaf94d2b43052d5753b54ab0291bed540cbe8d1a6ef7cac"},{"bytes":339,"kind":"generated","path":".claude-plugin/plugin.json","sha256":"bc4a32f7e5ff0d8839826a9d46816f3fc98c6706de3e3a9b06526814a564d873"},{"bytes":156,"kind":"generated","path":".codex-plugin/mcp.json","sha256":"4b442017d0d30ec207cef7a4e5d2cd9dca246080008633459a27e3a5f2f7935b"},{"bytes":1310,"kind":"generated","path":".codex-plugin/plugin.json","sha256":"5a048e6ec9e77b62c3fb26b082c12ba206cc4b066686c71425f3481c8c2e5a33"},{"bytes":332,"kind":"generated","path":".cursor-plugin/marketplace.json","sha256":"297ba8aebcdb8bcac814b520bf21d9d6acc9c8c94a1ecf3ca442a49d834aab59"},{"bytes":169,"kind":"generated","path":".cursor-plugin/mcp.json","sha256":"95c87d0f2ed3a4f6df7d0e519454b1234672eca030db3b8cd8776ef0f35dee74"},{"bytes":694,"kind":"generated","path":".cursor-plugin/plugin.json","sha256":"3ad2c4641736bf6ff91af4d8f5e9a1b4297aa8134e387d007154656eaa87ac5e"},{"bytes":363,"kind":"generated","path":".mcp.json","sha256":"5c56ac183c3d4fcbfa9386a73ab805495c6ab613b96bf71dbd5be80093e1a10b"},{"bytes":22732,"kind":"generated","path":"agent-bundle.compile-evidence.json","sha256":"65a8d94b188f73d99595258d706434a8b2c9c0d4b9ac3b879b9bbd19afd17bda"},{"bytes":1608688,"kind":"bundle","path":"bin/gbot-flight.mjs","sha256":"5e2211f62cd7501ba36f45ef1fc6577f91804f2daf77054d7308759261ff5441"},{"bytes":2483352,"kind":"bundle","mode":493,"path":"bin/gbot.mjs","sha256":"70ed28198daa2a204a649760e01fb60688d27f9aaac66de372e39ea0351a0cdd"},{"bytes":375,"kind":"generated","path":"commands/codex-send.md","sha256":"622af5d2945c3dc9fe3035b4e35a63cd6041e0b7a9570a5f5648ba857eae9074"},{"bytes":250,"kind":"generated","path":"commands/codex-threads.md","sha256":"a763a6e1d378d012cdf6f52bd5962cf8b3a0806ef5be88472ea199d250cf6a4b"},{"bytes":267,"kind":"generated","path":"commands/codex-wait.md","sha256":"f203f7e33ad2dd59723b1117fb65a5de062a16f8b7edda1b034f02e635c96f9a"},{"bytes":22762,"kind":"generated","path":"INSTALL.md","sha256":"f7b47cb4c9752966736551b712056dedc58c8405975451c151bd549e4625bd11"},{"bytes":78609,"kind":"generated","path":"install.mjs","sha256":"1f073590820c6ddc2ec33cb1c6d2aaed3dbdd00b319621bd7bfcac40a1bc1a2e"},{"bytes":246,"kind":"generated","path":"mcp.json","sha256":"c06a01fa851792f42e0cc807a444e454c3ea2fd164dc1c2adef455acc93879e2"},{"bytes":2275669,"kind":"bundle","path":"mcp/mcp-claude-channel-8029413c.mjs","sha256":"46c0e5abb42f0dcd1152ec9d6bf5fed42c3bcb109f89312bfaa9870ecc059fe3"},{"bytes":1550171,"kind":"bundle","path":"mcp/mcp-grok-bot-b8c2461e-flight.mjs","sha256":"2b55bb68ad9bb68c60be43623091ec1f59f143a94c7b877b7ef48ef2e53369cc"},{"bytes":3378016,"kind":"bundle","path":"mcp/mcp-grok-bot-b8c2461e.mjs","sha256":"28b7e5a8163764a5b68d6ac1814c8cf9cbc9df38218468c7fd0a82cda5d8f740"},{"bytes":681,"kind":"generated","path":"plugin.json","sha256":"a502feae7220144ce4ce55c18dea8926a062e5d902da897bc6fd62a6b247a90a"},{"bytes":953,"kind":"generated","path":"rules/codex-claude-on-user-machines.mdc","sha256":"a50ec93d021a807e0a18bf9311cdf103bc0ae3947e4c6a0bd78cbb941e8e5b24"},{"bytes":1317307,"kind":"bundle","path":"scripts/gbot-relay.mjs","sha256":"80bb1a3f22648ca3c7cf04cb238caa86711086261978833cd6494350ccda51be"},{"bytes":4856,"kind":"copy","path":"skills/chatgpt-desktop/SKILL.md","sha256":"0fe2560037f761203a317bf8755b0476ce864b7b1b13708559448d300aa4635c"},{"bytes":2134,"kind":"copy","path":"skills/codex-dot/SKILL.md","sha256":"7e910c3e9e74800c09c9643983cf51cd090ff6ba448800d2702968cd89c6a005"},{"bytes":6620,"kind":"copy","path":"skills/talk-to-grok-bot/references/bridge-administration.md","sha256":"b43f91464cdb885831bbf945766e4d9644fea017049ebcde3241a2da5ff0fc81"},{"bytes":5679,"kind":"copy","path":"skills/talk-to-grok-bot/SKILL.md","sha256":"33d526cf5cd5d458d5f5b80992f201918077fd0ca8c74b8e15fbe2dd78d435bb"}],"manifestVersion":6,"projections":[{"builtInHost":"claude","documents":{"marketplace":".claude-plugin/marketplace.json","mcp":".mcp.json","plugin":".claude-plugin/plugin.json"},"host":"claude","marketplace":{"name":"gbot-marketplace"}},{"builtInHost":"codex","documents":{"marketplace":".agents/plugins/marketplace.json","mcp":".codex-plugin/mcp.json","plugin":".codex-plugin/plugin.json"},"host":"codex","marketplace":{"name":"gbot-marketplace"}},{"builtInHost":"cursor","documents":{"marketplace":".cursor-plugin/marketplace.json","mcp":".cursor-plugin/mcp.json","plugin":".cursor-plugin/plugin.json"},"host":"cursor","marketplace":{"name":"gbot-marketplace"}},{"builtInHost":"portable","documents":{"mcp":"mcp.json","plugin":"plugin.json"},"host":"portable"}],"routes":{"cli":{"commands":[{"aliases":[],"description":"List current Grok approval cards (latest 200 entries).","exitCode":"zero","options":[{"key":"target","kind":"string","option":"target","positional":0,"repeated":false,"required":true}],"path":["approvals","list"],"routeId":"cli:approvals/list"},{"aliases":[],"description":"Explicit user decision: accept a Grok request once or decline; never grant persistent permissions.","exitCode":"zero","options":[{"choices":["accept","decline"],"key":"decision","kind":"enum","option":"decision","repeated":false,"required":true},{"key":"entryId","kind":"string","option":"entry-id","repeated":false,"required":true},{"key":"requestId","kind":"string","option":"request-id","repeated":false,"required":true},{"key":"target","kind":"string","option":"target","repeated":false,"required":true}],"path":["approvals","respond"],"routeId":"cli:approvals/respond"},{"aliases":[],"description":"Create a Grok Bot bot.","exitCode":"zero","options":[{"key":"avatarColor","kind":"string","option":"avatar-color","repeated":false,"required":false},{"key":"avatarShape","kind":"string","option":"avatar-shape","repeated":false,"required":false},{"key":"description","kind":"string","option":"description","repeated":false,"required":false},{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"key":"name","kind":"string","option":"name","repeated":false,"required":true},{"key":"title","kind":"string","option":"title","repeated":false,"required":false}],"path":["bots","create"],"routeId":"cli:bots/create"},{"aliases":[],"description":"Delete a bot or group by id or name.","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"description":"Bot or group id or name","key":"ref","kind":"string","option":"ref","positional":0,"repeated":false,"required":true}],"path":["bots","delete"],"routeId":"cli:bots/delete"},{"aliases":[],"description":"Show one bot or group by id or name.","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"description":"Bot or group id or name","key":"ref","kind":"string","option":"ref","positional":0,"repeated":false,"required":true}],"path":["bots","get"],"routeId":"cli:bots/get"},{"aliases":[],"description":"List Grok Bot bots (not groups).","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false}],"path":["bots","list"],"routeId":"cli:bots/list"},{"aliases":[],"description":"Update a bot or group profile.","exitCode":"zero","options":[{"key":"avatarColor","kind":"string","option":"avatar-color","repeated":false,"required":false},{"key":"avatarShape","kind":"string","option":"avatar-shape","repeated":false,"required":false},{"key":"description","kind":"string","option":"description","repeated":false,"required":false},{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"key":"hidden","kind":"string","option":"hidden","repeated":false,"required":false},{"key":"name","kind":"string","option":"name","repeated":false,"required":false},{"key":"notify","kind":"string","option":"notify","repeated":false,"required":false},{"description":"Bot or group id or name","key":"ref","kind":"string","option":"ref","positional":0,"repeated":false,"required":true},{"key":"title","kind":"string","option":"title","repeated":false,"required":false}],"path":["bots","update"],"routeId":"cli:bots/update"},{"aliases":[],"description":"Discover ChatGPT Desktop hosts and modelProviders at runtime (never hardcode). Always includes local plus every remote-thread-summaries-v3:; Providers come from distinct thread/list and remote-summary values.","exitCode":"result","options":[{"description":"Local CDP port","key":"port","kind":"number","option":"port","repeated":false,"required":false}],"path":["chatgpt-desktop","hosts"],"routeId":"cli:chatgpt-desktop/hosts"},{"aliases":[],"description":"Read a ChatGPT Desktop thread via app-server (turns/list); CDP DOM is fallback only.","exitCode":"result","options":[{"description":"Opaque nextCursor from the prior read page","key":"cursor","kind":"string","option":"cursor","repeated":false,"required":false},{"key":"full","kind":"boolean","option":"full","repeated":false,"required":false},{"description":"Recent default 100; full default 2000","key":"limit","kind":"number","option":"limit","repeated":false,"required":false},{"key":"openTimeoutMs","kind":"number","option":"open-timeout-ms","repeated":false,"required":false},{"key":"port","kind":"number","option":"port","repeated":false,"required":false},{"key":"threadId","kind":"string","option":"thread-id","positional":0,"repeated":false,"required":true}],"path":["chatgpt-desktop","read"],"routeId":"cli:chatgpt-desktop/read"},{"aliases":[],"description":"Search ChatGPT Desktop threads. --host and --model-provider accept any string (see gbot chatgpt-desktop hosts); not enums.","exitCode":"result","options":[{"description":"Opaque nextCursor from a prior search page","key":"cursor","kind":"string","option":"cursor","repeated":false,"required":false},{"choices":["host"],"description":"Group results by host (grouping mode, not a host id)","key":"groupBy","kind":"enum","option":"group-by","repeated":false,"required":false},{"description":"Any string: all (default), local, or a hostId/friendly name from `gbot chatgpt-desktop hosts`. Not an enum.","key":"host","kind":"string","option":"host","repeated":false,"required":false},{"description":"Max matches (1-200)","key":"limit","kind":"number","option":"limit","repeated":false,"required":false},{"description":"Any modelProvider id (app-server modelProviders pass-through). Omit = all. See `gbot chatgpt-desktop hosts`. Not an enum.","key":"modelProvider","kind":"string","option":"model-provider","repeated":false,"required":false},{"description":"Local CDP port","key":"port","kind":"number","option":"port","repeated":false,"required":false},{"description":"Project label or projectId filter.","key":"project","kind":"string","option":"project","repeated":false,"required":false},{"key":"query","kind":"string","option":"query","positional":0,"repeated":false,"required":true}],"path":["chatgpt-desktop","search"],"routeId":"cli:chatgpt-desktop/search"},{"aliases":[],"description":"Send to ChatGPT Desktop via CDP composer. Omit --thread-id for a new chat; --project prefers Start new chat in .","exitCode":"result","options":[{"key":"openTimeoutMs","kind":"number","option":"open-timeout-ms","repeated":false,"required":false},{"key":"port","kind":"number","option":"port","repeated":false,"required":false},{"key":"project","kind":"string","option":"project","repeated":false,"required":false},{"key":"text","kind":"string","option":"text","positional":0,"repeated":true,"required":true},{"key":"threadId","kind":"string","option":"thread-id","repeated":false,"required":false},{"description":"Explicitly unarchive an existing archived thread before sending.","key":"unarchive","kind":"boolean","option":"unarchive","repeated":false,"required":false}],"path":["chatgpt-desktop","send"],"routeId":"cli:chatgpt-desktop/send"},{"aliases":[],"description":"Probe local ChatGPT Desktop CDP on 127.0.0.1 and report app-server availability for list/deep-read.","exitCode":"result","options":[{"description":"Local CDP port (default 9222)","key":"port","kind":"number","option":"port","repeated":false,"required":false}],"path":["chatgpt-desktop","status"],"routeId":"cli:chatgpt-desktop/status"},{"aliases":[],"description":"List local + remote-control ChatGPT Desktop threads. --host and --model-provider accept any string (see gbot chatgpt-desktop hosts); not enums.","exitCode":"result","options":[{"description":"Opaque nextCursor from a previous page","key":"cursor","kind":"string","option":"cursor","repeated":false,"required":false},{"choices":["host"],"description":"Group results by host (grouping mode, not a host id)","key":"groupBy","kind":"enum","option":"group-by","repeated":false,"required":false},{"description":"Any string: all (default), local, or a hostId/friendly name from `gbot chatgpt-desktop hosts`. Not an enum.","key":"host","kind":"string","option":"host","repeated":false,"required":false},{"description":"Max threads (1-200)","key":"limit","kind":"number","option":"limit","repeated":false,"required":false},{"description":"Any modelProvider id (app-server modelProviders pass-through). Omit = all. See `gbot chatgpt-desktop hosts`. Not an enum.","key":"modelProvider","kind":"string","option":"model-provider","repeated":false,"required":false},{"description":"Local CDP port","key":"port","kind":"number","option":"port","repeated":false,"required":false},{"description":"Project label or projectId filter.","key":"project","kind":"string","option":"project","repeated":false,"required":false}],"path":["chatgpt-desktop","threads"],"routeId":"cli:chatgpt-desktop/threads"},{"aliases":[],"description":"Send to an explicitly enabled live Claude Code channel and wait for its reply.","exitCode":"result","options":[{"key":"message","kind":"string","option":"message","positional":1,"repeated":false,"required":true},{"key":"name","kind":"string","option":"name","positional":0,"repeated":false,"required":true},{"key":"timeoutMs","kind":"number","option":"timeout-ms","repeated":false,"required":false}],"path":["claude","send"],"routeId":"cli:claude/send"},{"aliases":[],"description":"Managed Grok/Codex bridge respond.","exitCode":"result","options":[{"description":"JSON object mapping each advertised question ID to {\"answers\":[\"answer\"]}; exact IDs required.","key":"answersJson","kind":"string","option":"answers-json","repeated":false,"required":false},{"key":"bindingId","kind":"string","option":"binding-id","repeated":false,"required":false},{"choices":["accept","decline","cancel"],"key":"decision","kind":"enum","option":"decision","repeated":false,"required":false},{"key":"exchangeId","kind":"string","option":"exchange-id","repeated":false,"required":false},{"key":"generation","kind":"string","option":"generation","repeated":false,"required":true},{"key":"interactionId","kind":"string","option":"interaction-id","repeated":false,"required":true},{"key":"threadId","kind":"string","option":"thread-id","repeated":false,"required":true},{"key":"turnId","kind":"string","option":"turn-id","repeated":false,"required":true}],"path":["codex","bridge","respond"],"routeId":"cli:codex/bridge/respond"},{"aliases":[],"description":"Run a bounded foreground relay (up to 23 hours). Use the packaged gbot-relay.mjs script for unlimited service lifetime.","exitCode":"result","options":[{"description":"Foreground lifetime in milliseconds (100..82800000; default 23h).","key":"lifetimeMs","kind":"number","option":"lifetime-ms","repeated":false,"required":false}],"path":["codex","bridge","run"],"routeId":"cli:codex/bridge/run"},{"aliases":[],"description":"Managed Grok/Codex bridge start.","exitCode":"result","options":[{"choices":["steer","reject"],"key":"busyPolicy","kind":"enum","option":"busy-policy","repeated":false,"required":false},{"key":"codexThreadId","kind":"string","option":"codex-thread-id","repeated":false,"required":false},{"key":"expectedCwd","kind":"string","option":"expected-cwd","repeated":false,"required":false},{"key":"grokTarget","kind":"string","option":"grok-target","positional":0,"repeated":false,"required":true},{"key":"requestId","kind":"string","option":"request-id","repeated":false,"required":false}],"path":["codex","bridge","start"],"routeId":"cli:codex/bridge/start"},{"aliases":[],"description":"Managed Grok/Codex bridge status.","exitCode":"result","options":[{"key":"bindingId","kind":"string","option":"binding-id","repeated":false,"required":false},{"key":"limit","kind":"number","option":"limit","repeated":false,"required":false}],"path":["codex","bridge","status"],"routeId":"cli:codex/bridge/status"},{"aliases":[],"description":"Managed Grok/Codex bridge stop.","exitCode":"result","options":[{"key":"all","kind":"boolean","option":"all","repeated":false,"required":false},{"key":"bindingId","kind":"string","option":"binding-id","repeated":false,"required":false},{"key":"worker","kind":"boolean","option":"worker","repeated":false,"required":false}],"path":["codex","bridge","stop"],"routeId":"cli:codex/bridge/stop"},{"aliases":[],"description":"Install, remove, or check the ChatGPT Desktop shim: a CODEX_CLI_PATH wrapper that bridges Desktop stdio onto the managed Codex daemon (stock app-server proxy hangs). macOS persists via LaunchAgent; always fails open to real Codex.","exitCode":"result","options":[{"choices":["install","uninstall","status"],"description":"install | uninstall | status","key":"action","kind":"enum","option":"action","positional":0,"repeated":false,"required":true}],"path":["codex","desktop-shim"],"routeId":"cli:codex/desktop-shim"},{"aliases":[],"description":"List Codex daemon-managed threads, newest activity first (all modelProviders; pages via --cursor / nextCursor). Find old threads with --query (name/title/id prefix, all pages), --active-within 7d, --since, --sort created.","exitCode":"result","options":[{"description":"Only threads updated within 90s, 30m, 12h, 7d, 2w","key":"activeWithin","kind":"string","option":"active-within","repeated":false,"required":false},{"description":"Archived threads only","key":"archived","kind":"boolean","option":"archived","repeated":false,"required":false},{"description":"Opaque pagination cursor from a previous nextCursor (may start with -)","key":"cursor","kind":"string","option":"cursor","repeated":false,"required":false},{"description":"Exact session cwd","key":"cwd","kind":"string","option":"cwd","repeated":false,"required":false},{"description":"Max threads to list (1-200)","key":"limit","kind":"number","option":"limit","repeated":false,"required":false},{"description":"Any modelProvider id; omit for all","key":"modelProvider","kind":"string","option":"model-provider","repeated":false,"required":false},{"choices":["desc","asc"],"key":"order","kind":"enum","option":"order","repeated":false,"required":false},{"description":"Case-insensitive substring of name/title, preview or id (id prefix works); scans every page","key":"query","kind":"string","option":"query","repeated":false,"required":false},{"description":"Only threads updated at/after this ISO date/time or epoch seconds","key":"since","kind":"string","option":"since","repeated":false,"required":false},{"choices":["updated","created","recency"],"description":"updated (last activity, default) | created | recency","key":"sort","kind":"enum","option":"sort","repeated":false,"required":false},{"description":"Any app-server source kind; omit for interactive sources","key":"sourceKind","kind":"string","option":"source-kind","repeated":false,"required":false}],"path":["codex","list-threads"],"routeId":"cli:codex/list-threads"},{"aliases":[],"description":"Start a new Codex thread in --cwd, optionally with --model, --effort and a message.","exitCode":"result","options":[{"key":"cwd","kind":"string","option":"cwd","repeated":false,"required":true},{"choices":["none","minimal","low","medium","high","xhigh"],"key":"effort","kind":"enum","option":"effort","repeated":false,"required":false},{"key":"expectedCwd","kind":"string","option":"expected-cwd","repeated":false,"required":false},{"key":"message","kind":"string","option":"message","positional":0,"repeated":true,"required":false},{"key":"model","kind":"string","option":"model","repeated":false,"required":false}],"path":["codex","new"],"routeId":"cli:codex/new"},{"aliases":[],"description":"List the experimental Codex thread queue (GROK_BOT_CODEX_EXPERIMENTAL=1).","exitCode":"result","options":[{"key":"threadId","kind":"string","option":"thread-id","positional":0,"repeated":false,"required":true}],"path":["codex","queue"],"routeId":"cli:codex/queue"},{"aliases":[],"description":"Send to Codex; acceptance is distinct from completion. Options precede threadId.","exitCode":"result","options":[{"key":"bindingId","kind":"string","option":"binding-id","repeated":false,"required":false},{"key":"correlationId","kind":"string","option":"correlation-id","repeated":false,"required":false},{"choices":["none","minimal","low","medium","high","xhigh"],"key":"effort","kind":"enum","option":"effort","repeated":false,"required":false},{"key":"envelope","kind":"boolean","option":"envelope","repeated":false,"required":false},{"key":"expectedCwd","kind":"string","option":"expected-cwd","repeated":false,"required":false},{"description":"Required active-turn guard for steer; stale guards reject.","key":"expectedTurnId","kind":"string","option":"expected-turn-id","repeated":false,"required":false},{"key":"hop","kind":"number","option":"hop","repeated":false,"required":false},{"description":"Reply budget: 1-4194304 bytes.","key":"maxOutputBytes","kind":"number","option":"max-output-bytes","repeated":false,"required":false},{"key":"message","kind":"string","option":"message","positional":1,"repeated":true,"required":true},{"key":"model","kind":"string","option":"model","repeated":false,"required":false},{"key":"replyTo","kind":"string","option":"reply-to","repeated":false,"required":false},{"key":"replyToGrok","kind":"string","option":"reply-to-grok","repeated":false,"required":false},{"key":"requestId","kind":"string","option":"request-id","repeated":false,"required":false},{"key":"threadId","kind":"string","option":"thread-id","positional":0,"repeated":false,"required":true},{"description":"Observation timeout: 1-7200000 milliseconds.","key":"timeoutMs","kind":"number","option":"timeout-ms","repeated":false,"required":false},{"key":"wait","kind":"boolean","option":"wait","repeated":false,"required":false},{"choices":["reject","queue","steer"],"key":"whenBusy","kind":"enum","option":"when-busy","repeated":false,"required":false}],"path":["codex","send"],"routeId":"cli:codex/send"},{"aliases":[],"description":"Probe the local Codex app-server daemon. Exit 0 only when it is usable.","exitCode":"result","options":[],"path":["codex","status"],"routeId":"cli:codex/status"},{"aliases":[],"description":"Bounded Codex wait observation; never interrupts execution.","exitCode":"result","options":[{"key":"expectedCwd","kind":"string","option":"expected-cwd","repeated":false,"required":false},{"description":"Reply budget: 1-4194304 bytes.","key":"maxOutputBytes","kind":"number","option":"max-output-bytes","repeated":false,"required":false},{"key":"messageId","kind":"string","option":"message-id","repeated":false,"required":false},{"key":"threadId","kind":"string","option":"thread-id","positional":0,"repeated":false,"required":true},{"description":"Observation timeout: 1-7200000 milliseconds.","key":"timeoutMs","kind":"number","option":"timeout-ms","repeated":false,"required":false},{"key":"turnId","kind":"string","option":"turn-id","positional":1,"repeated":false,"required":true}],"path":["codex","wait"],"routeId":"cli:codex/wait"},{"aliases":[],"description":"Bounded Codex watch observation; never interrupts execution.","exitCode":"result","options":[{"key":"expectedCwd","kind":"string","option":"expected-cwd","repeated":false,"required":false},{"description":"Maximum observed events: 1-500.","key":"maxEvents","kind":"number","option":"max-events","repeated":false,"required":false},{"key":"threadId","kind":"string","option":"thread-id","positional":0,"repeated":false,"required":true},{"description":"Observation timeout: 1-600000 milliseconds.","key":"timeoutMs","kind":"number","option":"timeout-ms","repeated":false,"required":false}],"path":["codex","watch"],"routeId":"cli:codex/watch"},{"aliases":[],"description":"Show which agents root and auth sources gbot can see.","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false}],"path":["doctor"],"routeId":"cli:doctor"},{"aliases":[],"description":"Add a bot to a group.","exitCode":"zero","options":[{"key":"bot","kind":"string","option":"bot","positional":1,"repeated":false,"required":true},{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"key":"group","kind":"string","option":"group","positional":0,"repeated":false,"required":true}],"path":["groups","add"],"routeId":"cli:groups/add"},{"aliases":[],"description":"Create a Grok Bot group.","exitCode":"zero","options":[{"key":"avatarColor","kind":"string","option":"avatar-color","repeated":false,"required":false},{"key":"avatarShape","kind":"string","option":"avatar-shape","repeated":false,"required":false},{"key":"description","kind":"string","option":"description","repeated":false,"required":false},{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"description":"Member bot id or name (repeatable)","key":"member","kind":"string","option":"member","repeated":true,"required":true},{"key":"name","kind":"string","option":"name","repeated":false,"required":true},{"key":"title","kind":"string","option":"title","repeated":false,"required":false}],"path":["groups","create"],"routeId":"cli:groups/create"},{"aliases":[],"description":"Delete a group by id or name.","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"description":"Group id or name","key":"ref","kind":"string","option":"ref","positional":0,"repeated":false,"required":true}],"path":["groups","delete"],"routeId":"cli:groups/delete"},{"aliases":[],"description":"Show one group by id or name.","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"description":"Group id or name","key":"ref","kind":"string","option":"ref","positional":0,"repeated":false,"required":true}],"path":["groups","get"],"routeId":"cli:groups/get"},{"aliases":[],"description":"List Grok Bot groups.","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false}],"path":["groups","list"],"routeId":"cli:groups/list"},{"aliases":[],"description":"Show members of a group (same payload as groups get).","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"description":"Group id or name","key":"ref","kind":"string","option":"ref","positional":0,"repeated":false,"required":true}],"path":["groups","members"],"routeId":"cli:groups/members"},{"aliases":[],"description":"Remove a bot from a group.","exitCode":"zero","options":[{"key":"bot","kind":"string","option":"bot","positional":1,"repeated":false,"required":true},{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"key":"group","kind":"string","option":"group","positional":0,"repeated":false,"required":true}],"path":["groups","remove"],"routeId":"cli:groups/remove"},{"aliases":[],"description":"Replace a group's member list.","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"key":"group","kind":"string","option":"group","positional":0,"repeated":false,"required":true},{"description":"Member bot id or name (repeatable)","key":"member","kind":"string","option":"member","repeated":true,"required":true}],"path":["groups","set"],"routeId":"cli:groups/set"},{"aliases":[],"description":"Update a group profile (members stay on set/add/remove).","exitCode":"zero","options":[{"key":"avatarColor","kind":"string","option":"avatar-color","repeated":false,"required":false},{"key":"avatarShape","kind":"string","option":"avatar-shape","repeated":false,"required":false},{"key":"description","kind":"string","option":"description","repeated":false,"required":false},{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"key":"hidden","kind":"string","option":"hidden","repeated":false,"required":false},{"key":"name","kind":"string","option":"name","repeated":false,"required":false},{"key":"notify","kind":"string","option":"notify","repeated":false,"required":false},{"description":"Group id or name","key":"ref","kind":"string","option":"ref","positional":0,"repeated":false,"required":true},{"key":"title","kind":"string","option":"title","repeated":false,"required":false}],"path":["groups","update"],"routeId":"cli:groups/update"},{"aliases":[],"description":"Read the opt-in local JSONL history without contacting the gateway.","exitCode":"zero","options":[{"description":"Directory containing history.jsonl","key":"historyDir","kind":"string","option":"history-dir","repeated":false,"required":false},{"description":"Maximum matching rows (1 or more)","key":"limit","kind":"number","option":"limit","repeated":false,"required":false},{"description":"Print the history file path","key":"path","kind":"boolean","option":"path","repeated":false,"required":false},{"description":"Bot or group id or name","key":"ref","kind":"string","option":"ref","positional":0,"repeated":false,"required":false},{"description":"Case-insensitive message text filter","key":"search","kind":"string","option":"search","repeated":false,"required":false}],"path":["history"],"routeId":"cli:history"},{"aliases":[],"description":"Send a message to a Grok Bot bot or group by name or id.","exitCode":"result","options":[{"key":"bindingId","kind":"string","option":"binding-id","repeated":false,"required":false},{"key":"codexThreadId","kind":"string","option":"codex-thread-id","repeated":false,"required":false},{"description":"Stable correlation id for multi-hop replies","key":"correlationId","kind":"string","option":"correlation-id","repeated":false,"required":false},{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Prepend the [gbot …] header","key":"envelope","kind":"boolean","option":"envelope","repeated":false,"required":false},{"key":"expectedCwd","kind":"string","option":"expected-cwd","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"description":"Directory containing history.jsonl","key":"historyDir","kind":"string","option":"history-dir","repeated":false,"required":false},{"description":"Hop count; refused at GROK_BOT_MAX_HOPS","key":"hop","kind":"number","option":"hop","repeated":false,"required":false},{"key":"message","kind":"string","option":"message","positional":1,"repeated":true,"required":true},{"description":"Skip local history for this command","key":"noHistory","kind":"boolean","option":"no-history","repeated":false,"required":false},{"choices":["auto","manual"],"key":"replyMode","kind":"enum","option":"reply-mode","repeated":false,"required":false},{"description":"Prior message id this send replies to","key":"replyTo","kind":"string","option":"reply-to","repeated":false,"required":false},{"key":"requestId","kind":"string","option":"request-id","repeated":false,"required":false},{"key":"target","kind":"string","option":"target","positional":0,"repeated":false,"required":true}],"path":["send"],"routeId":"cli:send"},{"aliases":[],"description":"Add a SKILL.md to the shared skill library. Every bot sees it.","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"description":"SKILL.md file, or a directory holding one","key":"path","kind":"string","option":"path","positional":0,"repeated":false,"required":true}],"path":["skills","add"],"routeId":"cli:skills/add"},{"aliases":[],"description":"List the skill library every bot in this Grok Bot shares.","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false}],"path":["skills","list"],"routeId":"cli:skills/list"},{"aliases":[],"description":"Remove a library skill by id or name. Every bot loses it.","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"description":"Skill id or name","key":"skill","kind":"string","option":"skill","positional":0,"repeated":false,"required":true}],"path":["skills","remove"],"routeId":"cli:skills/remove"},{"aliases":[],"description":"Read the most recent messages in a Grok Bot bot or group thread.","exitCode":"zero","options":[{"description":"Return entries after this opaque entry id","key":"after","kind":"string","option":"after","repeated":false,"required":false},{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Show full entry text in human output","key":"full","kind":"boolean","option":"full","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"description":"Directory containing history.jsonl","key":"historyDir","kind":"string","option":"history-dir","repeated":false,"required":false},{"description":"How many trailing entries to return (1-200)","key":"limit","kind":"number","option":"limit","repeated":false,"required":false},{"description":"Skip local history for this command","key":"noHistory","kind":"boolean","option":"no-history","repeated":false,"required":false},{"description":"Read one rooted thread by message id","key":"root","kind":"string","option":"root","repeated":false,"required":false},{"key":"target","kind":"string","option":"target","positional":0,"repeated":false,"required":true}],"path":["thread"],"routeId":"cli:thread"}],"mode":"generated","routes":[{"contract":"contract:src/cli/approvals/list.tsx#inputJsonSchema","description":"List current Grok approval cards (latest 200 entries).","id":"cli:approvals/list","inputSchema":{"additionalProperties":false,"properties":{"target":{"type":"string"}},"required":["target"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/approvals/list.tsx"},{"contract":"contract:src/cli/approvals/respond.tsx#inputJsonSchema","description":"Explicit user decision: accept a Grok request once or decline; never grant persistent permissions.","id":"cli:approvals/respond","inputSchema":{"additionalProperties":false,"properties":{"decision":{"enum":["accept","decline"],"type":"string"},"entryId":{"type":"string"},"requestId":{"type":"string"},"target":{"type":"string"}},"required":["target","entryId","requestId","decision"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/approvals/respond.tsx"},{"contract":"contract:src/cli/bots/create.tsx#inputJsonSchema","description":"Create a Grok Bot bot.","id":"cli:bots/create","inputSchema":{"additionalProperties":false,"properties":{"avatarColor":{"type":"string"},"avatarShape":{"type":"string"},"description":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"name":{"type":"string"},"title":{"type":"string"}},"required":["name"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/bots/create.tsx"},{"contract":"contract:src/cli/bots/delete.tsx#inputJsonSchema","description":"Delete a bot or group by id or name.","id":"cli:bots/delete","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"ref":{"description":"Bot or group id or name","type":"string"}},"required":["ref"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/bots/delete.tsx"},{"contract":"contract:src/cli/bots/get.tsx#inputJsonSchema","description":"Show one bot or group by id or name.","id":"cli:bots/get","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"ref":{"description":"Bot or group id or name","type":"string"}},"required":["ref"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/bots/get.tsx"},{"contract":"contract:src/cli/bots/list.tsx#inputJsonSchema","description":"List Grok Bot bots (not groups).","id":"cli:bots/list","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/bots/list.tsx"},{"contract":"contract:src/cli/bots/update.tsx#inputJsonSchema","description":"Update a bot or group profile.","id":"cli:bots/update","inputSchema":{"additionalProperties":false,"properties":{"avatarColor":{"type":"string"},"avatarShape":{"type":"string"},"description":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"hidden":{"type":"string"},"name":{"type":"string"},"notify":{"type":"string"},"ref":{"description":"Bot or group id or name","type":"string"},"title":{"type":"string"}},"required":["ref"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/bots/update.tsx"},{"contract":"contract:src/cli/chatgpt-desktop/hosts.tsx#inputJsonSchema","description":"Discover ChatGPT Desktop hosts and modelProviders at runtime (never hardcode). Always includes local plus every remote-thread-summaries-v3:; Providers come from distinct thread/list and remote-summary values.","id":"cli:chatgpt-desktop/hosts","inputSchema":{"additionalProperties":false,"properties":{"port":{"description":"Local CDP port","type":"number"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/chatgpt-desktop/hosts.tsx"},{"contract":"contract:src/cli/chatgpt-desktop/read.tsx#inputJsonSchema","description":"Read a ChatGPT Desktop thread via app-server (turns/list); CDP DOM is fallback only.","id":"cli:chatgpt-desktop/read","inputSchema":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from the prior read page","type":"string"},"full":{"default":false,"type":"boolean"},"limit":{"description":"Recent default 100; full default 2000","type":"number"},"openTimeoutMs":{"default":90000,"type":"number"},"port":{"type":"number"},"threadId":{"type":"string"}},"required":["threadId"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/chatgpt-desktop/read.tsx"},{"contract":"contract:src/cli/chatgpt-desktop/search.tsx#inputJsonSchema","description":"Search ChatGPT Desktop threads. --host and --model-provider accept any string (see gbot chatgpt-desktop hosts); not enums.","id":"cli:chatgpt-desktop/search","inputSchema":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from a prior search page","type":"string"},"groupBy":{"description":"Group results by host (grouping mode, not a host id)","enum":["host"],"type":"string"},"host":{"description":"Any string: all (default), local, or a hostId/friendly name from `gbot chatgpt-desktop hosts`. Not an enum.","type":"string"},"limit":{"default":50,"description":"Max matches (1-200)","type":"number"},"modelProvider":{"description":"Any modelProvider id (app-server modelProviders pass-through). Omit = all. See `gbot chatgpt-desktop hosts`. Not an enum.","type":"string"},"port":{"description":"Local CDP port","type":"number"},"project":{"description":"Project label or projectId filter.","type":"string"},"query":{"type":"string"}},"required":["query"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/chatgpt-desktop/search.tsx"},{"contract":"contract:src/cli/chatgpt-desktop/send.tsx#inputJsonSchema","description":"Send to ChatGPT Desktop via CDP composer. Omit --thread-id for a new chat; --project prefers Start new chat in .","id":"cli:chatgpt-desktop/send","inputSchema":{"additionalProperties":false,"properties":{"openTimeoutMs":{"type":"number"},"port":{"type":"number"},"project":{"type":"string"},"text":{"items":{"type":"string"},"type":"array"},"threadId":{"type":"string"},"unarchive":{"description":"Explicitly unarchive an existing archived thread before sending.","type":"boolean"}},"required":["text"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/chatgpt-desktop/send.tsx"},{"contract":"contract:src/cli/chatgpt-desktop/status.tsx#inputJsonSchema","description":"Probe local ChatGPT Desktop CDP on 127.0.0.1 and report app-server availability for list/deep-read.","id":"cli:chatgpt-desktop/status","inputSchema":{"additionalProperties":false,"properties":{"port":{"description":"Local CDP port (default 9222)","type":"number"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/chatgpt-desktop/status.tsx"},{"contract":"contract:src/cli/chatgpt-desktop/threads.tsx#inputJsonSchema","description":"List local + remote-control ChatGPT Desktop threads. --host and --model-provider accept any string (see gbot chatgpt-desktop hosts); not enums.","id":"cli:chatgpt-desktop/threads","inputSchema":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from a previous page","type":"string"},"groupBy":{"description":"Group results by host (grouping mode, not a host id)","enum":["host"],"type":"string"},"host":{"description":"Any string: all (default), local, or a hostId/friendly name from `gbot chatgpt-desktop hosts`. Not an enum.","type":"string"},"limit":{"default":50,"description":"Max threads (1-200)","type":"number"},"modelProvider":{"description":"Any modelProvider id (app-server modelProviders pass-through). Omit = all. See `gbot chatgpt-desktop hosts`. Not an enum.","type":"string"},"port":{"description":"Local CDP port","type":"number"},"project":{"description":"Project label or projectId filter.","type":"string"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/chatgpt-desktop/threads.tsx"},{"contract":"contract:src/cli/claude/send.tsx#inputJsonSchema","description":"Send to an explicitly enabled live Claude Code channel and wait for its reply.","id":"cli:claude/send","inputSchema":{"additionalProperties":false,"properties":{"message":{"type":"string"},"name":{"type":"string"},"timeoutMs":{"type":"number"}},"required":["name","message"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/claude/send.tsx"},{"contract":"contract:src/cli/codex/bridge/respond.tsx#inputJsonSchema","description":"Managed Grok/Codex bridge respond.","id":"cli:codex/bridge/respond","inputSchema":{"additionalProperties":false,"properties":{"answersJson":{"description":"JSON object mapping each advertised question ID to {\"answers\":[\"answer\"]}; exact IDs required.","type":"string"},"bindingId":{"type":"string"},"decision":{"enum":["accept","decline","cancel"],"type":"string"},"exchangeId":{"type":"string"},"generation":{"type":"string"},"interactionId":{"type":"string"},"threadId":{"type":"string"},"turnId":{"type":"string"}},"required":["interactionId","generation","threadId","turnId"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/bridge/respond.tsx"},{"contract":"contract:src/cli/codex/bridge/run.tsx#inputJsonSchema","description":"Run a bounded foreground relay (up to 23 hours). Use the packaged gbot-relay.mjs script for unlimited service lifetime.","id":"cli:codex/bridge/run","inputSchema":{"additionalProperties":false,"properties":{"lifetimeMs":{"description":"Foreground lifetime in milliseconds (100..82800000; default 23h).","type":"number"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/bridge/run.tsx"},{"contract":"contract:src/cli/codex/bridge/start.tsx#inputJsonSchema","description":"Managed Grok/Codex bridge start.","id":"cli:codex/bridge/start","inputSchema":{"additionalProperties":false,"properties":{"busyPolicy":{"enum":["steer","reject"],"type":"string"},"codexThreadId":{"type":"string"},"expectedCwd":{"type":"string"},"grokTarget":{"type":"string"},"requestId":{"type":"string"}},"required":["grokTarget"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/bridge/start.tsx"},{"contract":"contract:src/cli/codex/bridge/status.tsx#inputJsonSchema","description":"Managed Grok/Codex bridge status.","id":"cli:codex/bridge/status","inputSchema":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"limit":{"type":"number"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/bridge/status.tsx"},{"contract":"contract:src/cli/codex/bridge/stop.tsx#inputJsonSchema","description":"Managed Grok/Codex bridge stop.","id":"cli:codex/bridge/stop","inputSchema":{"additionalProperties":false,"properties":{"all":{"type":"boolean"},"bindingId":{"type":"string"},"worker":{"type":"boolean"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/bridge/stop.tsx"},{"contract":"contract:src/cli/codex/desktop-shim.tsx#inputJsonSchema","description":"Install, remove, or check the ChatGPT Desktop shim: a CODEX_CLI_PATH wrapper that bridges Desktop stdio onto the managed Codex daemon (stock app-server proxy hangs). macOS persists via LaunchAgent; always fails open to real Codex.","id":"cli:codex/desktop-shim","inputSchema":{"additionalProperties":false,"properties":{"action":{"description":"install | uninstall | status","enum":["install","uninstall","status"],"type":"string"}},"required":["action"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/desktop-shim.tsx"},{"contract":"contract:src/cli/codex/list-threads.tsx#inputJsonSchema","description":"List Codex daemon-managed threads, newest activity first (all modelProviders; pages via --cursor / nextCursor). Find old threads with --query (name/title/id prefix, all pages), --active-within 7d, --since, --sort created.","id":"cli:codex/list-threads","inputSchema":{"additionalProperties":false,"properties":{"activeWithin":{"description":"Only threads updated within 90s, 30m, 12h, 7d, 2w","type":"string"},"archived":{"description":"Archived threads only","type":"boolean"},"cursor":{"description":"Opaque pagination cursor from a previous nextCursor (may start with -)","type":"string"},"cwd":{"description":"Exact session cwd","type":"string"},"limit":{"default":20,"description":"Max threads to list (1-200)","type":"number"},"modelProvider":{"description":"Any modelProvider id; omit for all","type":"string"},"order":{"default":"desc","enum":["desc","asc"],"type":"string"},"query":{"description":"Case-insensitive substring of name/title, preview or id (id prefix works); scans every page","type":"string"},"since":{"description":"Only threads updated at/after this ISO date/time or epoch seconds","type":"string"},"sort":{"default":"updated","description":"updated (last activity, default) | created | recency","enum":["updated","created","recency"],"type":"string"},"sourceKind":{"description":"Any app-server source kind; omit for interactive sources","type":"string"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/list-threads.tsx"},{"contract":"contract:src/cli/codex/new.tsx#inputJsonSchema","description":"Start a new Codex thread in --cwd, optionally with --model, --effort and a message.","id":"cli:codex/new","inputSchema":{"additionalProperties":false,"properties":{"cwd":{"type":"string"},"effort":{"enum":["none","minimal","low","medium","high","xhigh"],"type":"string"},"expectedCwd":{"type":"string"},"message":{"items":{"type":"string"},"type":"array"},"model":{"type":"string"}},"required":["cwd"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/new.tsx"},{"contract":"contract:src/cli/codex/queue.tsx#inputJsonSchema","description":"List the experimental Codex thread queue (GROK_BOT_CODEX_EXPERIMENTAL=1).","id":"cli:codex/queue","inputSchema":{"additionalProperties":false,"properties":{"threadId":{"type":"string"}},"required":["threadId"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/queue.tsx"},{"contract":"contract:src/cli/codex/send.tsx#inputJsonSchema","description":"Send to Codex; acceptance is distinct from completion. Options precede threadId.","id":"cli:codex/send","inputSchema":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"correlationId":{"type":"string"},"effort":{"enum":["none","minimal","low","medium","high","xhigh"],"type":"string"},"envelope":{"type":"boolean"},"expectedCwd":{"type":"string"},"expectedTurnId":{"description":"Required active-turn guard for steer; stale guards reject.","type":"string"},"hop":{"type":"number"},"maxOutputBytes":{"description":"Reply budget: 1-4194304 bytes.","type":"number"},"message":{"items":{"type":"string"},"type":"array"},"model":{"type":"string"},"replyTo":{"type":"string"},"replyToGrok":{"type":"string"},"requestId":{"type":"string"},"threadId":{"type":"string"},"timeoutMs":{"description":"Observation timeout: 1-7200000 milliseconds.","type":"number"},"wait":{"type":"boolean"},"whenBusy":{"enum":["reject","queue","steer"],"type":"string"}},"required":["threadId","message"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/send.tsx"},{"contract":"contract:src/cli/codex/status.tsx#inputJsonSchema","description":"Probe the local Codex app-server daemon. Exit 0 only when it is usable.","id":"cli:codex/status","inputSchema":{"additionalProperties":false,"properties":{},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/status.tsx"},{"contract":"contract:src/cli/codex/wait.tsx#inputJsonSchema","description":"Bounded Codex wait observation; never interrupts execution.","id":"cli:codex/wait","inputSchema":{"additionalProperties":false,"properties":{"expectedCwd":{"type":"string"},"maxOutputBytes":{"description":"Reply budget: 1-4194304 bytes.","type":"number"},"messageId":{"type":"string"},"threadId":{"type":"string"},"timeoutMs":{"description":"Observation timeout: 1-7200000 milliseconds.","type":"number"},"turnId":{"type":"string"}},"required":["threadId","turnId"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/wait.tsx"},{"contract":"contract:src/cli/codex/watch.tsx#inputJsonSchema","description":"Bounded Codex watch observation; never interrupts execution.","id":"cli:codex/watch","inputSchema":{"additionalProperties":false,"properties":{"expectedCwd":{"type":"string"},"maxEvents":{"description":"Maximum observed events: 1-500.","type":"number"},"threadId":{"type":"string"},"timeoutMs":{"description":"Observation timeout: 1-600000 milliseconds.","type":"number"}},"required":["threadId"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/watch.tsx"},{"contract":"contract:src/cli/doctor.tsx#inputJsonSchema","description":"Show which agents root and auth sources gbot can see.","id":"cli:doctor","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/doctor.tsx"},{"contract":"contract:src/cli/groups/add.tsx#inputJsonSchema","description":"Add a bot to a group.","id":"cli:groups/add","inputSchema":{"additionalProperties":false,"properties":{"bot":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"group":{"type":"string"}},"required":["group","bot"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/groups/add.tsx"},{"contract":"contract:src/cli/groups/create.tsx#inputJsonSchema","description":"Create a Grok Bot group.","id":"cli:groups/create","inputSchema":{"additionalProperties":false,"properties":{"avatarColor":{"type":"string"},"avatarShape":{"type":"string"},"description":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"member":{"description":"Member bot id or name (repeatable)","items":{"type":"string"},"type":"array"},"name":{"type":"string"},"title":{"type":"string"}},"required":["name","member"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/groups/create.tsx"},{"contract":"contract:src/cli/groups/delete.tsx#inputJsonSchema","description":"Delete a group by id or name.","id":"cli:groups/delete","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"ref":{"description":"Group id or name","type":"string"}},"required":["ref"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/groups/delete.tsx"},{"contract":"contract:src/cli/groups/get.tsx#inputJsonSchema","description":"Show one group by id or name.","id":"cli:groups/get","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"ref":{"description":"Group id or name","type":"string"}},"required":["ref"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/groups/get.tsx"},{"contract":"contract:src/cli/groups/list.tsx#inputJsonSchema","description":"List Grok Bot groups.","id":"cli:groups/list","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/groups/list.tsx"},{"contract":"contract:src/cli/groups/members.tsx#inputJsonSchema","description":"Show members of a group (same payload as groups get).","id":"cli:groups/members","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"ref":{"description":"Group id or name","type":"string"}},"required":["ref"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/groups/members.tsx"},{"contract":"contract:src/cli/groups/remove.tsx#inputJsonSchema","description":"Remove a bot from a group.","id":"cli:groups/remove","inputSchema":{"additionalProperties":false,"properties":{"bot":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"group":{"type":"string"}},"required":["group","bot"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/groups/remove.tsx"},{"contract":"contract:src/cli/groups/set.tsx#inputJsonSchema","description":"Replace a group's member list.","id":"cli:groups/set","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"group":{"type":"string"},"member":{"description":"Member bot id or name (repeatable)","items":{"type":"string"},"type":"array"}},"required":["group","member"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/groups/set.tsx"},{"contract":"contract:src/cli/groups/update.tsx#inputJsonSchema","description":"Update a group profile (members stay on set/add/remove).","id":"cli:groups/update","inputSchema":{"additionalProperties":false,"properties":{"avatarColor":{"type":"string"},"avatarShape":{"type":"string"},"description":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"hidden":{"type":"string"},"name":{"type":"string"},"notify":{"type":"string"},"ref":{"description":"Group id or name","type":"string"},"title":{"type":"string"}},"required":["ref"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/groups/update.tsx"},{"contract":"contract:src/cli/history.tsx#inputJsonSchema","description":"Read the opt-in local JSONL history without contacting the gateway.","id":"cli:history","inputSchema":{"additionalProperties":false,"properties":{"historyDir":{"description":"Directory containing history.jsonl","type":"string"},"limit":{"default":40,"description":"Maximum matching rows (1 or more)","type":"number"},"path":{"description":"Print the history file path","type":"boolean"},"ref":{"description":"Bot or group id or name","type":"string"},"search":{"description":"Case-insensitive message text filter","type":"string"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/history.tsx"},{"contract":"contract:src/cli/send.tsx#inputJsonSchema","description":"Send a message to a Grok Bot bot or group by name or id.","id":"cli:send","inputSchema":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"codexThreadId":{"type":"string"},"correlationId":{"description":"Stable correlation id for multi-hop replies","type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"envelope":{"description":"Prepend the [gbot …] header","type":"boolean"},"expectedCwd":{"type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"historyDir":{"description":"Directory containing history.jsonl","type":"string"},"hop":{"description":"Hop count; refused at GROK_BOT_MAX_HOPS","type":"number"},"message":{"items":{"type":"string"},"type":"array"},"noHistory":{"description":"Skip local history for this command","type":"boolean"},"replyMode":{"enum":["auto","manual"],"type":"string"},"replyTo":{"description":"Prior message id this send replies to","type":"string"},"requestId":{"type":"string"},"target":{"type":"string"}},"required":["target","message"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/send.tsx"},{"contract":"contract:src/cli/skills/add.tsx#inputJsonSchema","description":"Add a SKILL.md to the shared skill library. Every bot sees it.","id":"cli:skills/add","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"path":{"description":"SKILL.md file, or a directory holding one","type":"string"}},"required":["path"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/skills/add.tsx"},{"contract":"contract:src/cli/skills/list.tsx#inputJsonSchema","description":"List the skill library every bot in this Grok Bot shares.","id":"cli:skills/list","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/skills/list.tsx"},{"contract":"contract:src/cli/skills/remove.tsx#inputJsonSchema","description":"Remove a library skill by id or name. Every bot loses it.","id":"cli:skills/remove","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"skill":{"description":"Skill id or name","type":"string"}},"required":["skill"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/skills/remove.tsx"},{"contract":"contract:src/cli/thread.tsx#inputJsonSchema","description":"Read the most recent messages in a Grok Bot bot or group thread.","id":"cli:thread","inputSchema":{"additionalProperties":false,"properties":{"after":{"description":"Return entries after this opaque entry id","type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"full":{"description":"Show full entry text in human output","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"historyDir":{"description":"Directory containing history.jsonl","type":"string"},"limit":{"default":40,"description":"How many trailing entries to return (1-200)","type":"number"},"noHistory":{"description":"Skip local history for this command","type":"boolean"},"root":{"description":"Read one rooted thread by message id","type":"string"},"target":{"type":"string"}},"required":["target"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/thread.tsx"}]},"contracts":[{"id":"contract:src/cli/approvals/list.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"target":{"type":"string"}},"required":["target"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/approvals/list.tsx"},"routes":["cli:approvals/list"]},{"id":"contract:src/cli/approvals/respond.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"decision":{"enum":["accept","decline"],"type":"string"},"entryId":{"type":"string"},"requestId":{"type":"string"},"target":{"type":"string"}},"required":["target","entryId","requestId","decision"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/approvals/respond.tsx"},"routes":["cli:approvals/respond"]},{"id":"contract:src/cli/bots/create.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"avatarColor":{"type":"string"},"avatarShape":{"type":"string"},"description":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"name":{"type":"string"},"title":{"type":"string"}},"required":["name"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/bots/create.tsx"},"routes":["cli:bots/create"]},{"id":"contract:src/cli/bots/delete.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"ref":{"description":"Bot or group id or name","type":"string"}},"required":["ref"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/bots/delete.tsx"},"routes":["cli:bots/delete"]},{"id":"contract:src/cli/bots/get.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"ref":{"description":"Bot or group id or name","type":"string"}},"required":["ref"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/bots/get.tsx"},"routes":["cli:bots/get"]},{"id":"contract:src/cli/bots/list.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/bots/list.tsx"},"routes":["cli:bots/list"]},{"id":"contract:src/cli/bots/update.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"avatarColor":{"type":"string"},"avatarShape":{"type":"string"},"description":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"hidden":{"type":"string"},"name":{"type":"string"},"notify":{"type":"string"},"ref":{"description":"Bot or group id or name","type":"string"},"title":{"type":"string"}},"required":["ref"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/bots/update.tsx"},"routes":["cli:bots/update"]},{"id":"contract:src/cli/chatgpt-desktop/hosts.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"port":{"description":"Local CDP port","type":"number"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/chatgpt-desktop/hosts.tsx"},"routes":["cli:chatgpt-desktop/hosts"]},{"id":"contract:src/cli/chatgpt-desktop/read.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from the prior read page","type":"string"},"full":{"default":false,"type":"boolean"},"limit":{"description":"Recent default 100; full default 2000","type":"number"},"openTimeoutMs":{"default":90000,"type":"number"},"port":{"type":"number"},"threadId":{"type":"string"}},"required":["threadId"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/chatgpt-desktop/read.tsx"},"routes":["cli:chatgpt-desktop/read"]},{"id":"contract:src/cli/chatgpt-desktop/search.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from a prior search page","type":"string"},"groupBy":{"description":"Group results by host (grouping mode, not a host id)","enum":["host"],"type":"string"},"host":{"description":"Any string: all (default), local, or a hostId/friendly name from `gbot chatgpt-desktop hosts`. Not an enum.","type":"string"},"limit":{"default":50,"description":"Max matches (1-200)","type":"number"},"modelProvider":{"description":"Any modelProvider id (app-server modelProviders pass-through). Omit = all. See `gbot chatgpt-desktop hosts`. Not an enum.","type":"string"},"port":{"description":"Local CDP port","type":"number"},"project":{"description":"Project label or projectId filter.","type":"string"},"query":{"type":"string"}},"required":["query"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/chatgpt-desktop/search.tsx"},"routes":["cli:chatgpt-desktop/search"]},{"id":"contract:src/cli/chatgpt-desktop/send.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"openTimeoutMs":{"type":"number"},"port":{"type":"number"},"project":{"type":"string"},"text":{"items":{"type":"string"},"type":"array"},"threadId":{"type":"string"},"unarchive":{"description":"Explicitly unarchive an existing archived thread before sending.","type":"boolean"}},"required":["text"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/chatgpt-desktop/send.tsx"},"routes":["cli:chatgpt-desktop/send"]},{"id":"contract:src/cli/chatgpt-desktop/status.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"port":{"description":"Local CDP port (default 9222)","type":"number"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/chatgpt-desktop/status.tsx"},"routes":["cli:chatgpt-desktop/status"]},{"id":"contract:src/cli/chatgpt-desktop/threads.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from a previous page","type":"string"},"groupBy":{"description":"Group results by host (grouping mode, not a host id)","enum":["host"],"type":"string"},"host":{"description":"Any string: all (default), local, or a hostId/friendly name from `gbot chatgpt-desktop hosts`. Not an enum.","type":"string"},"limit":{"default":50,"description":"Max threads (1-200)","type":"number"},"modelProvider":{"description":"Any modelProvider id (app-server modelProviders pass-through). Omit = all. See `gbot chatgpt-desktop hosts`. Not an enum.","type":"string"},"port":{"description":"Local CDP port","type":"number"},"project":{"description":"Project label or projectId filter.","type":"string"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/chatgpt-desktop/threads.tsx"},"routes":["cli:chatgpt-desktop/threads"]},{"id":"contract:src/cli/claude/send.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"message":{"type":"string"},"name":{"type":"string"},"timeoutMs":{"type":"number"}},"required":["name","message"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/claude/send.tsx"},"routes":["cli:claude/send"]},{"id":"contract:src/cli/codex/bridge/respond.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"answersJson":{"description":"JSON object mapping each advertised question ID to {\"answers\":[\"answer\"]}; exact IDs required.","type":"string"},"bindingId":{"type":"string"},"decision":{"enum":["accept","decline","cancel"],"type":"string"},"exchangeId":{"type":"string"},"generation":{"type":"string"},"interactionId":{"type":"string"},"threadId":{"type":"string"},"turnId":{"type":"string"}},"required":["interactionId","generation","threadId","turnId"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/bridge/respond.tsx"},"routes":["cli:codex/bridge/respond"]},{"id":"contract:src/cli/codex/bridge/run.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"lifetimeMs":{"description":"Foreground lifetime in milliseconds (100..82800000; default 23h).","type":"number"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/bridge/run.tsx"},"routes":["cli:codex/bridge/run"]},{"id":"contract:src/cli/codex/bridge/start.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"busyPolicy":{"enum":["steer","reject"],"type":"string"},"codexThreadId":{"type":"string"},"expectedCwd":{"type":"string"},"grokTarget":{"type":"string"},"requestId":{"type":"string"}},"required":["grokTarget"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/bridge/start.tsx"},"routes":["cli:codex/bridge/start"]},{"id":"contract:src/cli/codex/bridge/status.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"limit":{"type":"number"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/bridge/status.tsx"},"routes":["cli:codex/bridge/status"]},{"id":"contract:src/cli/codex/bridge/stop.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"all":{"type":"boolean"},"bindingId":{"type":"string"},"worker":{"type":"boolean"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/bridge/stop.tsx"},"routes":["cli:codex/bridge/stop"]},{"id":"contract:src/cli/codex/desktop-shim.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"action":{"description":"install | uninstall | status","enum":["install","uninstall","status"],"type":"string"}},"required":["action"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/desktop-shim.tsx"},"routes":["cli:codex/desktop-shim"]},{"id":"contract:src/cli/codex/list-threads.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"activeWithin":{"description":"Only threads updated within 90s, 30m, 12h, 7d, 2w","type":"string"},"archived":{"description":"Archived threads only","type":"boolean"},"cursor":{"description":"Opaque pagination cursor from a previous nextCursor (may start with -)","type":"string"},"cwd":{"description":"Exact session cwd","type":"string"},"limit":{"default":20,"description":"Max threads to list (1-200)","type":"number"},"modelProvider":{"description":"Any modelProvider id; omit for all","type":"string"},"order":{"default":"desc","enum":["desc","asc"],"type":"string"},"query":{"description":"Case-insensitive substring of name/title, preview or id (id prefix works); scans every page","type":"string"},"since":{"description":"Only threads updated at/after this ISO date/time or epoch seconds","type":"string"},"sort":{"default":"updated","description":"updated (last activity, default) | created | recency","enum":["updated","created","recency"],"type":"string"},"sourceKind":{"description":"Any app-server source kind; omit for interactive sources","type":"string"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/list-threads.tsx"},"routes":["cli:codex/list-threads"]},{"id":"contract:src/cli/codex/new.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"cwd":{"type":"string"},"effort":{"enum":["none","minimal","low","medium","high","xhigh"],"type":"string"},"expectedCwd":{"type":"string"},"message":{"items":{"type":"string"},"type":"array"},"model":{"type":"string"}},"required":["cwd"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/new.tsx"},"routes":["cli:codex/new"]},{"id":"contract:src/cli/codex/queue.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"threadId":{"type":"string"}},"required":["threadId"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/queue.tsx"},"routes":["cli:codex/queue"]},{"id":"contract:src/cli/codex/send.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"correlationId":{"type":"string"},"effort":{"enum":["none","minimal","low","medium","high","xhigh"],"type":"string"},"envelope":{"type":"boolean"},"expectedCwd":{"type":"string"},"expectedTurnId":{"description":"Required active-turn guard for steer; stale guards reject.","type":"string"},"hop":{"type":"number"},"maxOutputBytes":{"description":"Reply budget: 1-4194304 bytes.","type":"number"},"message":{"items":{"type":"string"},"type":"array"},"model":{"type":"string"},"replyTo":{"type":"string"},"replyToGrok":{"type":"string"},"requestId":{"type":"string"},"threadId":{"type":"string"},"timeoutMs":{"description":"Observation timeout: 1-7200000 milliseconds.","type":"number"},"wait":{"type":"boolean"},"whenBusy":{"enum":["reject","queue","steer"],"type":"string"}},"required":["threadId","message"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/send.tsx"},"routes":["cli:codex/send"]},{"id":"contract:src/cli/codex/status.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/status.tsx"},"routes":["cli:codex/status"]},{"id":"contract:src/cli/codex/wait.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"expectedCwd":{"type":"string"},"maxOutputBytes":{"description":"Reply budget: 1-4194304 bytes.","type":"number"},"messageId":{"type":"string"},"threadId":{"type":"string"},"timeoutMs":{"description":"Observation timeout: 1-7200000 milliseconds.","type":"number"},"turnId":{"type":"string"}},"required":["threadId","turnId"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/wait.tsx"},"routes":["cli:codex/wait"]},{"id":"contract:src/cli/codex/watch.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"expectedCwd":{"type":"string"},"maxEvents":{"description":"Maximum observed events: 1-500.","type":"number"},"threadId":{"type":"string"},"timeoutMs":{"description":"Observation timeout: 1-600000 milliseconds.","type":"number"}},"required":["threadId"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/watch.tsx"},"routes":["cli:codex/watch"]},{"id":"contract:src/cli/doctor.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/doctor.tsx"},"routes":["cli:doctor"]},{"id":"contract:src/cli/groups/add.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"bot":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"group":{"type":"string"}},"required":["group","bot"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/groups/add.tsx"},"routes":["cli:groups/add"]},{"id":"contract:src/cli/groups/create.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"avatarColor":{"type":"string"},"avatarShape":{"type":"string"},"description":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"member":{"description":"Member bot id or name (repeatable)","items":{"type":"string"},"type":"array"},"name":{"type":"string"},"title":{"type":"string"}},"required":["name","member"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/groups/create.tsx"},"routes":["cli:groups/create"]},{"id":"contract:src/cli/groups/delete.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"ref":{"description":"Group id or name","type":"string"}},"required":["ref"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/groups/delete.tsx"},"routes":["cli:groups/delete"]},{"id":"contract:src/cli/groups/get.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"ref":{"description":"Group id or name","type":"string"}},"required":["ref"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/groups/get.tsx"},"routes":["cli:groups/get"]},{"id":"contract:src/cli/groups/list.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/groups/list.tsx"},"routes":["cli:groups/list"]},{"id":"contract:src/cli/groups/members.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"ref":{"description":"Group id or name","type":"string"}},"required":["ref"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/groups/members.tsx"},"routes":["cli:groups/members"]},{"id":"contract:src/cli/groups/remove.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"bot":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"group":{"type":"string"}},"required":["group","bot"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/groups/remove.tsx"},"routes":["cli:groups/remove"]},{"id":"contract:src/cli/groups/set.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"group":{"type":"string"},"member":{"description":"Member bot id or name (repeatable)","items":{"type":"string"},"type":"array"}},"required":["group","member"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/groups/set.tsx"},"routes":["cli:groups/set"]},{"id":"contract:src/cli/groups/update.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"avatarColor":{"type":"string"},"avatarShape":{"type":"string"},"description":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"hidden":{"type":"string"},"name":{"type":"string"},"notify":{"type":"string"},"ref":{"description":"Group id or name","type":"string"},"title":{"type":"string"}},"required":["ref"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/groups/update.tsx"},"routes":["cli:groups/update"]},{"id":"contract:src/cli/history.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"historyDir":{"description":"Directory containing history.jsonl","type":"string"},"limit":{"default":40,"description":"Maximum matching rows (1 or more)","type":"number"},"path":{"description":"Print the history file path","type":"boolean"},"ref":{"description":"Bot or group id or name","type":"string"},"search":{"description":"Case-insensitive message text filter","type":"string"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/history.tsx"},"routes":["cli:history"]},{"id":"contract:src/cli/send.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"codexThreadId":{"type":"string"},"correlationId":{"description":"Stable correlation id for multi-hop replies","type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"envelope":{"description":"Prepend the [gbot …] header","type":"boolean"},"expectedCwd":{"type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"historyDir":{"description":"Directory containing history.jsonl","type":"string"},"hop":{"description":"Hop count; refused at GROK_BOT_MAX_HOPS","type":"number"},"message":{"items":{"type":"string"},"type":"array"},"noHistory":{"description":"Skip local history for this command","type":"boolean"},"replyMode":{"enum":["auto","manual"],"type":"string"},"replyTo":{"description":"Prior message id this send replies to","type":"string"},"requestId":{"type":"string"},"target":{"type":"string"}},"required":["target","message"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/send.tsx"},"routes":["cli:send"]},{"id":"contract:src/cli/skills/add.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"path":{"description":"SKILL.md file, or a directory holding one","type":"string"}},"required":["path"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/skills/add.tsx"},"routes":["cli:skills/add"]},{"id":"contract:src/cli/skills/list.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/skills/list.tsx"},"routes":["cli:skills/list"]},{"id":"contract:src/cli/skills/remove.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"skill":{"description":"Skill id or name","type":"string"}},"required":["skill"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/skills/remove.tsx"},"routes":["cli:skills/remove"]},{"id":"contract:src/cli/thread.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"after":{"description":"Return entries after this opaque entry id","type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"full":{"description":"Show full entry text in human output","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"historyDir":{"description":"Directory containing history.jsonl","type":"string"},"limit":{"default":40,"description":"How many trailing entries to return (1-200)","type":"number"},"noHistory":{"description":"Skip local history for this command","type":"boolean"},"root":{"description":"Read one rooted thread by message id","type":"string"},"target":{"type":"string"}},"required":["target"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/thread.tsx"},"routes":["cli:thread"]},{"id":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"port":{"type":"number"}},"required":[],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx"},"routes":["tool:grok-bot/chatgpt_desktop_list_hosts"]},{"id":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_list_threads.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from the prior page; keep filters unchanged.","type":"string"},"groupBy":{"description":"When \"host\", also return groups[] keyed by host (grouping mode, not a host id).","enum":["host"],"type":"string"},"host":{"description":"Any string filter for machine/location. Reserved: \"all\" (default), \"local\". Otherwise a hostId or friendly name discovered at runtime — see chatgpt_desktop_list_hosts. Not an enum; new machines/connections appear with no code change.","type":"string"},"limit":{"default":50,"description":"Maximum threads per page.","type":"number"},"modelProvider":{"description":"Any modelProvider id string (passed through to app-server modelProviders). Omit for all providers. Discovered values: see chatgpt_desktop_list_hosts. Not an enum.","type":"string"},"port":{"type":"number"},"project":{"description":"Match a Desktop project label or app-server projectId. Dynamic string, not an enum.","type":"string"}},"required":[],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/chatgpt_desktop_list_threads.tsx"},"routes":["tool:grok-bot/chatgpt_desktop_list_threads"]},{"id":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_open_thread.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"openTimeoutMs":{"description":"Maximum time to wait for the requested conversation (1-600000 ms).","type":"number"},"port":{"description":"Local CDP port (default 9222 or CHATGPT_DESKTOP_CDP_PORT).","type":"number"},"threadId":{"description":"local: or bare durable id. Temporary local:client-new-thread:* works only while selected; remote ids require their owning host.","type":"string"}},"required":["threadId"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/chatgpt_desktop_open_thread.tsx"},"routes":["tool:grok-bot/chatgpt_desktop_open_thread"]},{"id":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_read_thread.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from the prior read page. Keep threadId and full unchanged.","type":"string"},"full":{"description":"When true, page from the start through the complete history (up to 2000 turns by default); otherwise return the most recent 100 turns by default. Explicit limit bounds either mode.","type":"boolean"},"limit":{"type":"number"},"openTimeoutMs":{"description":"Max wait for Loading task… to clear after opening (default 90000).","type":"number"},"port":{"type":"number"},"threadId":{"description":"local:, bare durable id, or selected local:client-new-thread:* temporary id. Remote ids return REMOTE_THREAD_NOT_LOADED with hostId.","type":"string"}},"required":["threadId"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/chatgpt_desktop_read_thread.tsx"},"routes":["tool:grok-bot/chatgpt_desktop_read_thread"]},{"id":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_search_threads.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from the previous search page; keep query and filters unchanged.","type":"string"},"groupBy":{"description":"When \"host\", also return groups[] keyed by host (grouping mode, not a host id).","enum":["host"],"type":"string"},"host":{"description":"Any string filter for machine/location. Reserved: \"all\" (default), \"local\". Otherwise a hostId or friendly name discovered at runtime — see chatgpt_desktop_list_hosts. Not an enum; new machines/connections appear with no code change.","type":"string"},"limit":{"default":50,"description":"Maximum matches.","type":"number"},"modelProvider":{"description":"Any modelProvider id string (passed through to app-server modelProviders). Omit for all providers. Discovered values: see chatgpt_desktop_list_hosts. Not an enum.","type":"string"},"port":{"type":"number"},"project":{"description":"Match a Desktop project label or app-server projectId.","type":"string"},"query":{"description":"Case-insensitive substring over thread metadata.","type":"string"}},"required":["query"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/chatgpt_desktop_search_threads.tsx"},"routes":["tool:grok-bot/chatgpt_desktop_search_threads"]},{"id":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_send.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"openTimeoutMs":{"type":"number"},"port":{"type":"number"},"project":{"description":"With threadId omitted, create the new chat inside this project. Omit for a new chat outside projects.","type":"string"},"text":{"type":"string"},"threadId":{"description":"local:, bare durable id, or selected local:client-new-thread:*; omit for a new chat.","type":"string"},"unarchive":{"default":false,"description":"Existing threadId only: explicitly call thread/unarchive after ARCHIVED_THREAD, then retry the send once. Never implicit.","type":"boolean"}},"required":["text"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/chatgpt_desktop_send.tsx"},"routes":["tool:grok-bot/chatgpt_desktop_send"]},{"id":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_status.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"port":{"description":"Local CDP port (default 9222 or CHATGPT_DESKTOP_CDP_PORT).","type":"number"}},"required":[],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/chatgpt_desktop_status.tsx"},"routes":["tool:grok-bot/chatgpt_desktop_status"]},{"id":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_wait_reply.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"port":{"type":"number"},"threadId":{"description":"Durable local:, bare id, or selected local:client-new-thread:* from send.","type":"string"},"timeoutMs":{"default":120000,"type":"number"}},"required":[],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/chatgpt_desktop_wait_reply.tsx"},"routes":["tool:grok-bot/chatgpt_desktop_wait_reply"]},{"id":"contract:src/mcp/grok-bot/tools/claude_send.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"message":{"type":"string"},"name":{"type":"string"},"timeoutMs":{"type":"number"}},"required":["name","message"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/claude_send.tsx"},"routes":["tool:grok-bot/claude_send"]},{"id":"contract:src/mcp/grok-bot/tools/codex_new.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"cwd":{"type":"string"},"effort":{"enum":["none","minimal","low","medium","high","xhigh"],"type":"string"},"expectedCwd":{"type":"string"},"message":{"type":"string"},"model":{"type":"string"}},"required":["cwd"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/codex_new.tsx"},"routes":["tool:grok-bot/codex_new"]},{"id":"contract:src/mcp/grok-bot/tools/codex_send.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"correlationId":{"type":"string"},"effort":{"enum":["none","minimal","low","medium","high","xhigh"],"type":"string"},"envelope":{"type":"boolean"},"expectedCwd":{"type":"string"},"expectedTurnId":{"type":"string"},"hop":{"type":"number"},"maxOutputBytes":{"type":"number"},"message":{"type":"string"},"model":{"type":"string"},"replyTo":{"type":"string"},"replyToGrok":{"type":"string"},"requestId":{"type":"string"},"threadId":{"type":"string"},"timeoutMs":{"type":"number"},"wait":{"default":false,"type":"boolean"},"whenBusy":{"enum":["reject","queue","steer"],"type":"string"}},"required":["threadId","message"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/codex_send.tsx"},"routes":["tool:grok-bot/codex_send"]},{"id":"contract:src/mcp/grok-bot/tools/codex_threads.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"activeWithin":{"description":"Only threads updated within this long: 90s, 30m, 12h, 7d, 2w.","type":"string"},"archived":{"description":"true = archived threads only.","type":"boolean"},"cursor":{"description":"Opaque nextCursor from a previous page.","type":"string"},"cwd":{"description":"Exact session working directory.","type":"string"},"limit":{"description":"Maximum threads in this page: 1-200 (default 20). Every provider is included; use cursor/nextCursor to page.","type":"number"},"modelProvider":{"description":"Any modelProvider id; omit for all.","type":"string"},"order":{"default":"desc","enum":["desc","asc"],"type":"string"},"query":{"description":"Case-insensitive substring of thread name/title, preview or id (id prefix works). Scans all pages.","type":"string"},"since":{"description":"Only threads updated at or after this ISO date/time or epoch seconds.","type":"string"},"sort":{"default":"updated","description":"updated = last activity (default), created = creation time, recency = daemon recency order.","enum":["updated","created","recency"],"type":"string"},"sourceKind":{"description":"Any app-server source kind (cli, vscode, exec, appServer, subAgent...); omit for interactive sources.","type":"string"}},"required":[],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/codex_threads.tsx"},"routes":["tool:grok-bot/codex_threads"]},{"id":"contract:src/mcp/grok-bot/tools/codex_wait.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"expectedCwd":{"type":"string"},"maxOutputBytes":{"description":"Reply budget: 1-4194304 bytes.","type":"number"},"messageId":{"type":"string"},"threadId":{"type":"string"},"timeoutMs":{"description":"Observation timeout: 1-7200000 milliseconds.","type":"number"},"turnId":{"type":"string"}},"required":["threadId","turnId"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/codex_wait.tsx"},"routes":["tool:grok-bot/codex_wait"]},{"id":"contract:src/mcp/grok-bot/tools/codex_watch.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"expectedCwd":{"type":"string"},"maxEvents":{"description":"Maximum observed events: 1-500.","type":"number"},"threadId":{"type":"string"},"timeoutMs":{"description":"Observation timeout: 1-600000 milliseconds.","type":"number"}},"required":["threadId"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/codex_watch.tsx"},"routes":["tool:grok-bot/codex_watch"]},{"id":"contract:src/mcp/grok-bot/tools/gbot_bridge_start.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"busyPolicy":{"enum":["steer","reject"],"type":"string"},"codexThreadId":{"type":"string"},"expectedCwd":{"type":"string"},"grokTarget":{"type":"string"},"requestId":{"type":"string"}},"required":["grokTarget"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/gbot_bridge_start.tsx"},"routes":["tool:grok-bot/gbot_bridge_start"]},{"id":"contract:src/mcp/grok-bot/tools/gbot_bridge_status.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"limit":{"type":"number"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/gbot_bridge_status.tsx"},"routes":["tool:grok-bot/gbot_bridge_status"]},{"id":"contract:src/mcp/grok-bot/tools/gbot_bridge_stop.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"all":{"type":"boolean"},"bindingId":{"type":"string"},"worker":{"type":"boolean"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/gbot_bridge_stop.tsx"},"routes":["tool:grok-bot/gbot_bridge_stop"]},{"id":"contract:src/mcp/grok-bot/tools/gbot_codex_respond.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"answersJson":{"description":"JSON object mapping each advertised question ID to {\"answers\":[\"answer\"]}; exact IDs required.","type":"string"},"bindingId":{"type":"string"},"decision":{"enum":["accept","decline","cancel"],"type":"string"},"exchangeId":{"type":"string"},"generation":{"type":"string"},"interactionId":{"type":"string"},"threadId":{"type":"string"},"turnId":{"type":"string"}},"required":["interactionId","generation","threadId","turnId"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/gbot_codex_respond.tsx"},"routes":["tool:grok-bot/gbot_codex_respond"]},{"id":"contract:src/mcp/grok-bot/tools/gbot_grok_approvals.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"target":{"type":"string"}},"required":["target"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/gbot_grok_approvals.tsx"},"routes":["tool:grok-bot/gbot_grok_approvals"]},{"id":"contract:src/mcp/grok-bot/tools/gbot_grok_respond.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"decision":{"enum":["accept","decline"],"type":"string"},"entryId":{"type":"string"},"requestId":{"type":"string"},"target":{"type":"string"}},"required":["target","entryId","requestId","decision"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/gbot_grok_respond.tsx"},"routes":["tool:grok-bot/gbot_grok_respond"]},{"id":"contract:src/mcp/grok-bot/tools/gbot_send.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"codexThreadId":{"type":"string"},"correlationId":{"type":"string"},"expectedCwd":{"type":"string"},"hop":{"description":"Explicit chain hop count, refused at the configured bound.","type":"number"},"message":{"type":"string"},"replyMode":{"enum":["auto","manual"],"type":"string"},"requestId":{"type":"string"},"target":{"type":"string"}},"required":["target","message"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/gbot_send.tsx"},"routes":["tool:grok-bot/gbot_send"]},{"id":"contract:src/mcp/grok-bot/tools/gbot_thread.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"after":{"description":"Opaque cursor from the previous call. Returns entries strictly after it; an unknown cursor resets with a bounded snapshot.","type":"string"},"full":{"default":false,"description":"Return entries with text up to bounded budgets (20k chars per entry, 200k total). Every entry reports truncated/fullLength; read any remainder with `gbot thread --full` / `--json` on the machine.","type":"boolean"},"limit":{"default":40,"description":"How many trailing entries to inspect (1-200). Entries are returned only with full:true.","type":"number"},"target":{"description":"Bot or group name or id, for example \"General\".","type":"string"}},"required":["target"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/gbot_thread.tsx"},"routes":["tool:grok-bot/gbot_thread"]}],"digest":"bc29982af3710b2b65dd33190d3d1c7f8a92e2425e9d015d74e0cf698ceca8c2","events":[],"layouts":[],"providers":[],"scripts":[{"id":"script:gbot-relay","kind":"script","provenance":{"kind":"conventional"},"source":"src/scripts/gbot-relay.ts"}],"servers":[{"id":"mcp:grok-bot","mode":"generated","name":"grok-bot","routes":[{"contract":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx#inputJsonSchema","description":"Discover local, remote-control environment ids, managed SSH hosts, and modelProvider ids. hostName is null when Desktop has no real display name. Use hostId or label in list/search filters; remote thread ids must be read on the owning host. warnings report provider discovery failures.","id":"tool:grok-bot/chatgpt_desktop_list_hosts","inputSchema":{"additionalProperties":false,"properties":{"port":{"type":"number"}},"required":[],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_list_threads.tsx#inputJsonSchema","description":"List one sorted Desktop inventory without duplicate rows. Pass nextCursor as cursor with unchanged filters until null; pages fill limit unless the MCP byte budget binds. A changed/expired inventory cursor requires restarting. Display titles are capped at 200 characters with titleTruncated:true. Filter by dynamic hostId/hostName, modelProvider, or Desktop project label/projectId; projectRootPath identifies the resolved workspace. Rows expose local:, location, hostId, and provider; remote rows require their owning host for reading or sending. Backend and warnings identify partial sources.","id":"tool:grok-bot/chatgpt_desktop_list_threads","inputSchema":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from the prior page; keep filters unchanged.","type":"string"},"groupBy":{"description":"When \"host\", also return groups[] keyed by host (grouping mode, not a host id).","enum":["host"],"type":"string"},"host":{"description":"Any string filter for machine/location. Reserved: \"all\" (default), \"local\". Otherwise a hostId or friendly name discovered at runtime — see chatgpt_desktop_list_hosts. Not an enum; new machines/connections appear with no code change.","type":"string"},"limit":{"default":50,"description":"Maximum threads per page.","type":"number"},"modelProvider":{"description":"Any modelProvider id string (passed through to app-server modelProviders). Omit for all providers. Discovered values: see chatgpt_desktop_list_hosts. Not an enum.","type":"string"},"port":{"type":"number"},"project":{"description":"Match a Desktop project label or app-server projectId. Dynamic string, not an enum.","type":"string"}},"required":[],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/chatgpt_desktop_list_threads.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_open_thread.tsx#inputJsonSchema","description":"Navigate Desktop to an existing local: or bare conversation id, even when the row is absent from the sidebar. Archived routes return archived:true without unarchiving; read them with chatgpt_desktop_read_thread. A remote-control id is owned by its hostId and cannot be opened on this Mac. CDP_UNREACHABLE means Desktop must be reachable locally.","id":"tool:grok-bot/chatgpt_desktop_open_thread","inputSchema":{"additionalProperties":false,"properties":{"openTimeoutMs":{"description":"Maximum time to wait for the requested conversation (1-600000 ms).","type":"number"},"port":{"description":"Local CDP port (default 9222 or CHATGPT_DESKTOP_CDP_PORT).","type":"number"},"threadId":{"description":"local: or bare durable id. Temporary local:client-new-thread:* works only while selected; remote ids require their owning host.","type":"string"}},"required":["threadId"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/chatgpt_desktop_open_thread.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_read_thread.tsx#inputJsonSchema","description":"Read a local:, bare durable id, or selected temporary local:client-new-thread:* id. Pass nextCursor back as cursor with the same threadId/full mode until complete:true. Pages stop at a byte budget; oversized turns use ordered continuation fragments with stable turnKey and field offsets, without dropping text. Archived threads remain readable. Warnings explain partial pages or app-server errors. REMOTE_THREAD_NOT_LOADED includes the owning hostId: call on that host.","id":"tool:grok-bot/chatgpt_desktop_read_thread","inputSchema":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from the prior read page. Keep threadId and full unchanged.","type":"string"},"full":{"description":"When true, page from the start through the complete history (up to 2000 turns by default); otherwise return the most recent 100 turns by default. Explicit limit bounds either mode.","type":"boolean"},"limit":{"type":"number"},"openTimeoutMs":{"description":"Max wait for Loading task… to clear after opening (default 90000).","type":"number"},"port":{"type":"number"},"threadId":{"description":"local:, bare durable id, or selected local:client-new-thread:* temporary id. Remote ids return REMOTE_THREAD_NOT_LOADED with hostId.","type":"string"}},"required":["threadId"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/chatgpt_desktop_read_thread.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_search_threads.tsx#inputJsonSchema","description":"Search full Desktop thread metadata by case-insensitive substring. Display titles are capped at 200 characters with titleTruncated:true. Pass nextCursor as cursor with unchanged query and filters to retrieve all matches. Dynamic hostId/hostName, modelProvider, and project filters apply. Remote results identify hostId and cannot be read on this Mac.","id":"tool:grok-bot/chatgpt_desktop_search_threads","inputSchema":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from the previous search page; keep query and filters unchanged.","type":"string"},"groupBy":{"description":"When \"host\", also return groups[] keyed by host (grouping mode, not a host id).","enum":["host"],"type":"string"},"host":{"description":"Any string filter for machine/location. Reserved: \"all\" (default), \"local\". Otherwise a hostId or friendly name discovered at runtime — see chatgpt_desktop_list_hosts. Not an enum; new machines/connections appear with no code change.","type":"string"},"limit":{"default":50,"description":"Maximum matches.","type":"number"},"modelProvider":{"description":"Any modelProvider id string (passed through to app-server modelProviders). Omit for all providers. Discovered values: see chatgpt_desktop_list_hosts. Not an enum.","type":"string"},"port":{"type":"number"},"project":{"description":"Match a Desktop project label or app-server projectId.","type":"string"},"query":{"description":"Case-insensitive substring over thread metadata.","type":"string"}},"required":["query"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/chatgpt_desktop_search_threads.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_send.tsx#inputJsonSchema","description":"Send once through local Desktop CDP. With threadId, accept local:, bare durable id, or currently selected local:client-new-thread:*; remote ids must be sent on their hostId. Omit threadId and project for a new chat outside projects; omit threadId and set project for a new chat inside it. The empty view and project scope are checked before typing. New-thread receipts include temporaryThreadId when seen and return durable local: once resolved. ARCHIVED_THREAD rejects without sending; unarchive:true explicitly unarchives that existing thread first. PROJECT_UNAVAILABLE identifies a disabled project action or missing configured root; NEW_CHAT_NAVIGATION_FAILED reports an unchanged view promptly. CDP_UNREACHABLE and COMPOSER_HAS_DRAFT require operator inspection, not blind retry.","id":"tool:grok-bot/chatgpt_desktop_send","inputSchema":{"additionalProperties":false,"properties":{"openTimeoutMs":{"type":"number"},"port":{"type":"number"},"project":{"description":"With threadId omitted, create the new chat inside this project. Omit for a new chat outside projects.","type":"string"},"text":{"type":"string"},"threadId":{"description":"local:, bare durable id, or selected local:client-new-thread:*; omit for a new chat.","type":"string"},"unarchive":{"default":false,"description":"Existing threadId only: explicitly call thread/unarchive after ARCHIVED_THREAD, then retry the send once. Never implicit.","type":"boolean"}},"required":["text"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/chatgpt_desktop_send.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_status.tsx#inputJsonSchema","description":"Check local ChatGPT Desktop CDP before send, wait, or open. reachable:false and exitCode:1 mean CDP is down even if appServerFallback.reachable is true; list/search/read may still work through app-server. There is no remote CDP transport.","id":"tool:grok-bot/chatgpt_desktop_status","inputSchema":{"additionalProperties":false,"properties":{"port":{"description":"Local CDP port (default 9222 or CHATGPT_DESKTOP_CDP_PORT).","type":"number"}},"required":[],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/chatgpt_desktop_status.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_wait_reply.tsx#inputJsonSchema","description":"After chatgpt_desktop_send, wait for the selected Desktop reply over CDP. Accept the returned durable local: or the temporary local:client-new-thread:* while selected; omit threadId only when staying on the same selected chat. Returns reply, durable threadId and conversationId. timeout is not proof the send failed; CDP_UNREACHABLE requires Desktop recovery. Remote ids must be handled on their hostId.","id":"tool:grok-bot/chatgpt_desktop_wait_reply","inputSchema":{"additionalProperties":false,"properties":{"port":{"type":"number"},"threadId":{"description":"Durable local:, bare id, or selected local:client-new-thread:* from send.","type":"string"},"timeoutMs":{"default":120000,"type":"number"}},"required":[],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/chatgpt_desktop_wait_reply.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/claude_send.tsx#inputJsonSchema","description":"Send to a named, opted-in live Claude Code channel and wait for its reply. Unknown delivery must not be retried automatically. Runs on the user's registered machine via its local socket, not the Grok Bot box. From the box, use Grok Bot Shell with a machineId to run gbot there after codex app-server daemon start or bootstrap; gbot has no remote transport.","id":"tool:grok-bot/claude_send","inputSchema":{"additionalProperties":false,"properties":{"message":{"type":"string"},"name":{"type":"string"},"timeoutMs":{"type":"number"}},"required":["name","message"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/claude_send.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/codex_new.tsx#inputJsonSchema","description":"Start a new Codex thread on this machine in cwd, optionally sending its first message. Codex must run locally on the same machine as gbot.","id":"tool:grok-bot/codex_new","inputSchema":{"additionalProperties":false,"properties":{"cwd":{"type":"string"},"effort":{"enum":["none","minimal","low","medium","high","xhigh"],"type":"string"},"expectedCwd":{"type":"string"},"message":{"type":"string"},"model":{"type":"string"}},"required":["cwd"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/codex_new.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/codex_send.tsx#inputJsonSchema","description":"Send to a Codex thread. Runs on the user's registered machine via its local socket, not the Grok Bot box. From the box, use Grok Bot Shell with a machineId to run gbot there after codex app-server daemon start or bootstrap; gbot has no remote transport. With replyToGrok or bindingId, managed delivery returns the terminal answer to Grok automatically. Otherwise optional wait observes completion and explicit steer requires expectedTurnId. Acceptance is not completion.","id":"tool:grok-bot/codex_send","inputSchema":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"correlationId":{"type":"string"},"effort":{"enum":["none","minimal","low","medium","high","xhigh"],"type":"string"},"envelope":{"type":"boolean"},"expectedCwd":{"type":"string"},"expectedTurnId":{"type":"string"},"hop":{"type":"number"},"maxOutputBytes":{"type":"number"},"message":{"type":"string"},"model":{"type":"string"},"replyTo":{"type":"string"},"replyToGrok":{"type":"string"},"requestId":{"type":"string"},"threadId":{"type":"string"},"timeoutMs":{"type":"number"},"wait":{"default":false,"type":"boolean"},"whenBusy":{"enum":["reject","queue","steer"],"type":"string"}},"required":["threadId","message"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/codex_send.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/codex_threads.tsx#inputJsonSchema","description":"Discover Codex daemon threads on the user's registered machine (local socket, not the Grok Bot box; from the box run gbot there through Shell with a machineId after codex app-server daemon start or bootstrap). Newest activity first by default (sort:\"updated\"), so old-but-active threads surface; sort:\"created\" restores creation order. Find one thread with query (case-insensitive substring of name/title, preview or id, so an id prefix works) - it scans every page, not just the first 100. Narrow with activeWithin (\"30m\", \"12h\", \"7d\", \"2w\") or since (ISO/epoch seconds), cwd, modelProvider, sourceKind, archived. limit is the page size (1-200); pass nextCursor back as cursor for more.","id":"tool:grok-bot/codex_threads","inputSchema":{"additionalProperties":false,"properties":{"activeWithin":{"description":"Only threads updated within this long: 90s, 30m, 12h, 7d, 2w.","type":"string"},"archived":{"description":"true = archived threads only.","type":"boolean"},"cursor":{"description":"Opaque nextCursor from a previous page.","type":"string"},"cwd":{"description":"Exact session working directory.","type":"string"},"limit":{"description":"Maximum threads in this page: 1-200 (default 20). Every provider is included; use cursor/nextCursor to page.","type":"number"},"modelProvider":{"description":"Any modelProvider id; omit for all.","type":"string"},"order":{"default":"desc","enum":["desc","asc"],"type":"string"},"query":{"description":"Case-insensitive substring of thread name/title, preview or id (id prefix works). Scans all pages.","type":"string"},"since":{"description":"Only threads updated at or after this ISO date/time or epoch seconds.","type":"string"},"sort":{"default":"updated","description":"updated = last activity (default), created = creation time, recency = daemon recency order.","enum":["updated","created","recency"],"type":"string"},"sourceKind":{"description":"Any app-server source kind (cli, vscode, exec, appServer, subAgent...); omit for interactive sources.","type":"string"}},"required":[],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/codex_threads.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/codex_wait.tsx#inputJsonSchema","description":"Explicit diagnostic observation of one Codex turn; returns execution and final reply without interrupting it. Runs on the user's registered machine via its local socket, not the Grok Bot box. From the box, use Grok Bot Shell with a machineId to run gbot there after codex app-server daemon start or bootstrap; gbot has no remote transport.","id":"tool:grok-bot/codex_wait","inputSchema":{"additionalProperties":false,"properties":{"expectedCwd":{"type":"string"},"maxOutputBytes":{"description":"Reply budget: 1-4194304 bytes.","type":"number"},"messageId":{"type":"string"},"threadId":{"type":"string"},"timeoutMs":{"description":"Observation timeout: 1-7200000 milliseconds.","type":"number"},"turnId":{"type":"string"}},"required":["threadId","turnId"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/codex_wait.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/codex_watch.tsx#inputJsonSchema","description":"Watch bounded Codex thread events for diagnostics without answering approvals. Runs on the user's registered machine via its local socket, not the Grok Bot box. From the box, use Grok Bot Shell with a machineId to run gbot there after codex app-server daemon start or bootstrap; gbot has no remote transport.","id":"tool:grok-bot/codex_watch","inputSchema":{"additionalProperties":false,"properties":{"expectedCwd":{"type":"string"},"maxEvents":{"description":"Maximum observed events: 1-500.","type":"number"},"threadId":{"type":"string"},"timeoutMs":{"description":"Observation timeout: 1-600000 milliseconds.","type":"number"}},"required":["threadId"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/codex_watch.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/gbot_bridge_start.tsx#inputJsonSchema","description":"Link a Grok conversation to Codex once. New visible Grok messages arrive automatically and Codex final answers return to Grok.","id":"tool:grok-bot/gbot_bridge_start","inputSchema":{"additionalProperties":false,"properties":{"busyPolicy":{"enum":["steer","reject"],"type":"string"},"codexThreadId":{"type":"string"},"expectedCwd":{"type":"string"},"grokTarget":{"type":"string"},"requestId":{"type":"string"}},"required":["grokTarget"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/gbot_bridge_start.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/gbot_bridge_status.tsx#inputJsonSchema","description":"Inspect worker health, route coverage, bounded receipts and scoped pending operator interactions.","id":"tool:grok-bot/gbot_bridge_status","inputSchema":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"limit":{"type":"number"}},"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/gbot_bridge_status.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/gbot_bridge_stop.tsx#inputJsonSchema","description":"Stop a binding without deleting receipts or interrupting Codex. all stops all bindings; worker explicitly shuts down the worker.","id":"tool:grok-bot/gbot_bridge_stop","inputSchema":{"additionalProperties":false,"properties":{"all":{"type":"boolean"},"bindingId":{"type":"string"},"worker":{"type":"boolean"}},"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/gbot_bridge_stop.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/gbot_codex_respond.tsx#inputJsonSchema","description":"Explicit operator response to a current scoped Codex interaction. Supports only one-time accept/decline/cancel or exact question-ID answers. Never auto-approve.","id":"tool:grok-bot/gbot_codex_respond","inputSchema":{"additionalProperties":false,"properties":{"answersJson":{"description":"JSON object mapping each advertised question ID to {\"answers\":[\"answer\"]}; exact IDs required.","type":"string"},"bindingId":{"type":"string"},"decision":{"enum":["accept","decline","cancel"],"type":"string"},"exchangeId":{"type":"string"},"generation":{"type":"string"},"interactionId":{"type":"string"},"threadId":{"type":"string"},"turnId":{"type":"string"}},"required":["interactionId","generation","threadId","turnId"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/gbot_codex_respond.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/gbot_grok_approvals.tsx#inputJsonSchema","description":"List pending auto-review and local-tool approval cards in the latest 200 entries for a Grok bot. Older or unsupported requests require the owning Grok UI.","id":"tool:grok-bot/gbot_grok_approvals","inputSchema":{"additionalProperties":false,"properties":{"target":{"type":"string"}},"required":["target"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/gbot_grok_approvals.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/gbot_grok_respond.tsx#inputJsonSchema","description":"Only after an explicit user decision: accept one current Grok approval once or decline it. Exact target, entryId and approval requestId required. Never auto-approve or grant persistent permissions. Success acknowledges response delivery, not execution.","id":"tool:grok-bot/gbot_grok_respond","inputSchema":{"additionalProperties":false,"properties":{"decision":{"enum":["accept","decline"],"type":"string"},"entryId":{"type":"string"},"requestId":{"type":"string"},"target":{"type":"string"}},"required":["target","entryId","requestId","decision"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/gbot_grok_respond.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/gbot_send.tsx#inputJsonSchema","description":"Send to Grok Bot. Native Codex calls automatically receive replies in their originating thread; send once and continue work. Without a native source, supply codexThreadId or use manual gbot_thread reading.","id":"tool:grok-bot/gbot_send","inputSchema":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"codexThreadId":{"type":"string"},"correlationId":{"type":"string"},"expectedCwd":{"type":"string"},"hop":{"description":"Explicit chain hop count, refused at the configured bound.","type":"number"},"message":{"type":"string"},"replyMode":{"enum":["auto","manual"],"type":"string"},"requestId":{"type":"string"},"target":{"type":"string"}},"required":["target","message"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/gbot_send.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/gbot_thread.tsx#inputJsonSchema","description":"Read a bounded Grok Bot thread tail. Returns a small receipt by default; pass the last cursor as after for an exclusive client-side delta, or full:true to include bounded entry text.","id":"tool:grok-bot/gbot_thread","inputSchema":{"additionalProperties":false,"properties":{"after":{"description":"Opaque cursor from the previous call. Returns entries strictly after it; an unknown cursor resets with a bounded snapshot.","type":"string"},"full":{"default":false,"description":"Return entries with text up to bounded budgets (20k chars per entry, 200k total). Every entry reports truncated/fullLength; read any remainder with `gbot thread --full` / `--json` on the machine.","type":"boolean"},"limit":{"default":40,"description":"How many trailing entries to inspect (1-200). Entries are returned only with full:true.","type":"number"},"target":{"description":"Bot or group name or id, for example \"General\".","type":"string"}},"required":["target"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/gbot_thread.tsx"}]}]},"runtime":{"node":"22.19.0"}} +{"application":{"description":"Message Grok Bot from Codex, Claude Code, and Cursor. Codex/Claude tools use local sockets on the user's registered machines only (no remote transport); from the Grok Bot box, run gbot via Grok Bot Shell with a machineId.","id":"plugin:gbot","name":"gbot","version":"0.12.4"},"compiler":{"adapters":[{"adapterRevision":"1.28.0","host":"claude","observedVersion":"2.1.260","schemas":[{"name":"hooks","revision":"2.1.260","sha256":"0cdfc5eb5201f2c3091768559ac82f4c563ccb1b7bce7a39d5f99e5f404654cb"},{"name":"lsp","revision":"2.1.260","sha256":"b4419c5d857267c7e2b21e3e1eb98b4fdc302c87109007190d3258e6ba7096e4"},{"name":"marketplace","revision":"2.1.260","sha256":"31ee4cc43ba5ce2be248030a69da2d31171550b2cc51c1fcb9f788a8ab92783d"},{"name":"mcp","revision":"2.1.260","sha256":"edd4770e41d6aee5beae1ff918d33139613a243e454ecbd52b72fa824be4a662"},{"name":"monitors","revision":"2.1.260","sha256":"6378d94b51fb7c784eaee178237008a03da0f889d0520d57486784954a1d484c"},{"name":"plugin","revision":"2.1.260","sha256":"2a976091b81ad07ae8eca57f6f9c5749efeba17fa089a0c12b6ca84d6e70f118"},{"name":"settings","revision":"2.1.260","sha256":"2bbca553621dbf9433a9b7d1ff7952543a368434f2d999a4e4e360b95b6d4c3d"},{"name":"theme","revision":"2.1.260","sha256":"9931264e6f5a1d4b3b854ce7a17d602c9ba1b57cc1991f1d3991b316eaa81ac2"}]},{"adapterRevision":"1.13.0","host":"codex","observedVersion":"0.147.0","schemas":[{"name":"app","revision":"0.147.0","sha256":"01c720a645e437bf0c4f8c26fd4cb5a13988e5649e4a8562ee23a1d4b7355c6a"},{"name":"hooks","revision":"0.147.0","sha256":"175b859eb8e85bd287d85ee840d97c3f5c2d0dda3223507a796d158e3770eeba"},{"name":"marketplace","revision":"0.147.0","sha256":"fbccce3ade39e1b077fcb440e60260de6e811e8c2d222b2ae0fe8fe47706b470"},{"name":"mcp","revision":"0.147.0","sha256":"75bd50f9fcb85c2e8d43bc132d61c172a02f28ea8bb77389816ae77b14a4257e"},{"name":"plugin","revision":"0.147.0","sha256":"074c6c71966a3e6560ccbceb8d82ec6a40cb1eccee2f2d863fb4ef1e2276a814"}]},{"adapterRevision":"1.13.0","host":"cursor","observedVersion":"2026-08-28","schemas":[{"name":"hooks","revision":"2026-08-28","sha256":"06154b7afa0861df462130b988912b897e7ccf962b8dd20c09193100bcde5d81"},{"name":"marketplace","revision":"2026-08-28","sha256":"1aae96a24c2796419933bc8bfe3a1255394e7199c35740b36325e0ce6dbc253d"},{"name":"mcp","revision":"2026-08-28","sha256":"f3fa4615afefe004c4fbcc09e635d890df0f1ec0cb39540feab72cbd3a31d844"},{"name":"plugin","revision":"2026-08-28","sha256":"a393b758901803fcf5cfe0d77bda8a83e987d32c3377dfce2d9edf445af884ed"}]},{"adapterRevision":"1.10.0","host":"portable","observedVersion":"1.0.0","schemas":[{"name":"mcp","revision":"1.0.0","sha256":"6539175bfcdf43085855183e86da40ea94b166547a72b47ae9a0a390516d3acb"},{"name":"plugin","revision":"1.0.0","sha256":"0a4aad95ce337878ad38802ebf0daa3fde76abe3f65400c86bcbb1ec0b3ab883"}]}],"agentSkills":{"schemaSha256":"6d06b61e423317421de2295ea1fd0c7b4491bfa36c5b7705c28616dfe76d4047","sourceRevision":"69ef37e9424c0a7ea9dd2293b559e43ec8176379","specification":"https://raw.githubusercontent.com/agentskills/agentskills/69ef37e9424c0a7ea9dd2293b559e43ec8176379/docs/specification.mdx"},"producer":{"name":"agent-bundle","version":"0.4.0"},"project":{"configDigest":"7718be28a54eb7ec9e28b9653ef671683604436af21e1eda99c11098f43e278f","configPath":"agent-bundle.config.ts","modelDigest":"3deade4a0e41ae311d1b34ee00195bb15ebc6b2c83fcfd67514b0f8f068c8b74","packageName":"grok-bot-cli","packageVersion":"0.12.4","revision":"0945ab0af9e1e853985c3a23f669d63071ed7335075f2cdd0325dbde101e20f4","sourceInputs":[{"executable":false,"path":".changeset/config.json","sha256":"0a97ce5d0488bada887ee9ea3726edecc727454b7da0294f1b5fe4947f48ee7c"},{"executable":false,"path":".changeset/desktop-startup-passthrough.md","sha256":"ca8295f090a5b9fc6e7e80fbb013e9518e4b2c1dcc882f6227e17838ccc0178d"},{"executable":false,"path":".changeset/mcp-server-patch.md","sha256":"446138a1bb0325fbe49a415709e09df47ea8e2775d3c4dc9c11725b116cd29d8"},{"executable":false,"path":".changeset/README.md","sha256":"495bd4805f8362853bce99689c1cd75db8c475dfa38930619d2e197d7554db27"},{"executable":false,"path":".github/workflows/changeset.yml","sha256":"0139beb8a3dc0e95126c9701d09251a879331b1e8c5540bbfc69848525b40f23"},{"executable":false,"path":".github/workflows/ci.yml","sha256":"17a58cf5ceff03b7922d15c9b0eea11ae3e0fd00a581ff6881f69cf795f3ec0b"},{"executable":false,"path":".github/workflows/release.yml","sha256":"9c97360a2221fca9317edcac0eb343298c765e3b34bd048f916c47533dd90311"},{"executable":false,"path":".gitignore","sha256":"30aaeac24084f0a642d811aa493fc51b0fd3eddff322d359c39c573d8394ff3e"},{"executable":false,"path":".npmrc","sha256":"da47110e4c2d7cd216cd495b604ca78bd0f5caaca32da4837ec0efa6f967bba0"},{"executable":false,"path":"agent-bundle.config.ts","sha256":"7718be28a54eb7ec9e28b9653ef671683604436af21e1eda99c11098f43e278f"},{"executable":false,"path":"AGENTS.md","sha256":"e18ce90a031c533e7226789b97f6bce4aa7994b672544e0f425d20cc3bc15d29"},{"executable":false,"path":"CHANGELOG.md","sha256":"8913d85a39ba09eee5b057c851115242dbbc31a573abf65d00724594fba7e1de"},{"executable":false,"path":"demo/grok-bot-cli-demo.gif","sha256":"0fe01e15fd2abbbc5cc3313e42931d404d3a320764e22fdcfb995f1a45dd73cd"},{"executable":false,"path":"demo/grok-bot-cli-demo.mp4","sha256":"b6ed66e43616674096c2f0a4a4da46fe4693c681833eab013f872a66b9ff933c"},{"executable":false,"path":"docs/chatgpt-desktop-cdp.md","sha256":"6dd6cfe00e9a888c1ced79f1db85744f9c92affef8a78126e22ea5205e76b732"},{"executable":false,"path":"docs/codex-busy-threads.md","sha256":"42509e8e4c2b519949f06d4e7028b8973d56f8fc0cd98decdb231375bb49d38d"},{"executable":false,"path":"docs/codex-duplex-architecture.md","sha256":"792f1be8bad508b042c5813c1fc3b7ea5ac87032803227830666727306e55fa2"},{"executable":false,"path":"docs/verification/2026-09-15-duplex.md","sha256":"c7d97f709a3c2d851c0a24690321bbda161ec925b4143a456b9951e174bf75df"},{"executable":false,"path":"LICENSE","sha256":"bc48c8a0f6b6e45842e574c79a6b955fe683ed13f48a50cc08bcccdb1a59fe59"},{"executable":false,"path":"package-lock.json","sha256":"f96cdb413a9939f2e0895fbc3fe855ef5da196d4e50354796978cade170e36fc"},{"executable":false,"path":"package.json","sha256":"39870136f51c0d1279cdf1deba3fae7f2e57b66a8119fa55753e01bfdb5b2cef"},{"executable":false,"path":"README.md","sha256":"5e33cf4acf3c737da2b4f51a0bf31e405c7e74d4c5b2148684465e41062a96b4"},{"executable":false,"path":"renovate.json","sha256":"e80b4e42a3043bc12fa0640db4bac392d2bf770acf841360d7c8ceeeac2ec1a9"},{"executable":false,"path":"rstest.route-unit.config.ts","sha256":"7286e20dc3d4162673e7c3dfe824db00307e1fda178410f23044a97286d141f2"},{"executable":false,"path":"scripts/run-unit-tests.mjs","sha256":"483f91b4856ce294a483dd2e62ca53bf09301adbc4c5fc9571da0e1d28049bc6"},{"executable":false,"path":"src/cli/_shared.ts","sha256":"555210759d9b2e2e60a01452c137abd73bad42bfab8b2311b66a3ab97f4ee5e9"},{"executable":false,"path":"src/cli/approvals/list.tsx","sha256":"8d21320b6cd9d6b2a266fd30196b8a439960f865a6f0a5dacf5845d64ed7388c"},{"executable":false,"path":"src/cli/approvals/respond.tsx","sha256":"7d9fe6e641deb08f444684733daac6da47fb472d90ed0e9ff77892f376a87255"},{"executable":false,"path":"src/cli/bots/create.tsx","sha256":"9f7a73a0595b642dd77f008ad596bcafd00fc5846ec1abff2ce62819d983eb84"},{"executable":false,"path":"src/cli/bots/delete.tsx","sha256":"126b53ac1da4a657f22d1a6770ddbab2cc66715c811de0873d50016bbe39cbf2"},{"executable":false,"path":"src/cli/bots/get.tsx","sha256":"f0c752a3b498819083a8ee5aa918d275727f1cc94d8774d8c84ac74db66e8a71"},{"executable":false,"path":"src/cli/bots/list.tsx","sha256":"21c0f7e39005860dfc2514b71f745bb4cab29d409ef2cff26f8759bb0fd261c1"},{"executable":false,"path":"src/cli/bots/update.tsx","sha256":"33b7f5aafbf931cf3cb5ecf423a76dacd09adb117a7d5490050639ae7f4210dd"},{"executable":false,"path":"src/cli/chatgpt-desktop/hosts.tsx","sha256":"64e3e873115a4f5c2b6a7fd68236dbf2ca4e4a880ba97bdd7409b48931bca632"},{"executable":false,"path":"src/cli/chatgpt-desktop/read.tsx","sha256":"11c86f604c2ee405f7544394db60ee1b36917d0b90769d441fcd361b5f0fe038"},{"executable":false,"path":"src/cli/chatgpt-desktop/search.tsx","sha256":"0dc3acdea178ff14d6c1c4b8b3bf1e18af47984f63b8ed8c41483e4da4b72f08"},{"executable":false,"path":"src/cli/chatgpt-desktop/send.tsx","sha256":"b801a028980e60fe37dca56e86887a1e2b0b8ce80ce77b379e7375d2e4544d5e"},{"executable":false,"path":"src/cli/chatgpt-desktop/status.tsx","sha256":"141b7b6db6d6f9236dd5343ca9495200752937b05764ad7fa7261631c2db5bbc"},{"executable":false,"path":"src/cli/chatgpt-desktop/threads.tsx","sha256":"d0a33935d992af349c52ef448622da5e460b4af1a9474aac60994295245b43ad"},{"executable":false,"path":"src/cli/claude/send.tsx","sha256":"d650caadec248aa59530d124ca6186c45d9f9fe1fb0f9c805e9eef6dc9f06d92"},{"executable":false,"path":"src/cli/codex/bridge/respond.tsx","sha256":"9d3a6af4904af6e44583dfb4ef317b3fe1045a78e765fc27406eaca9bcadcd13"},{"executable":false,"path":"src/cli/codex/bridge/run.tsx","sha256":"a49d334bebb00db62ad2c54370a47ec2c9119b4c07eb6ba11fc4bba4dd8e4217"},{"executable":false,"path":"src/cli/codex/bridge/start.tsx","sha256":"9e423cf5d65168606f6235baa2bcd127eb949647f7a4dee284d75f831474524d"},{"executable":false,"path":"src/cli/codex/bridge/status.tsx","sha256":"c801f69080985c87025429fb136487acdfac093acb07241b3417ea9a83915a18"},{"executable":false,"path":"src/cli/codex/bridge/stop.tsx","sha256":"6710e298a21d124c442d5aa1a3744280e5cd4df93213797f2ea3c0654174184a"},{"executable":false,"path":"src/cli/codex/desktop-shim.tsx","sha256":"fa3d88d11895bc4a15700bd590c055aa392b2b0e0c1185c52da233a23589aba0"},{"executable":false,"path":"src/cli/codex/list-threads.tsx","sha256":"6675fb6ef19acb63375bf47d302083d4bde055ce3dfc8c9db39664f3ec68b0f1"},{"executable":false,"path":"src/cli/codex/new.tsx","sha256":"11d0658c1a3e7173d53b71446b184fccccd867baefc6f733e43a9d543ca26ec9"},{"executable":false,"path":"src/cli/codex/queue.tsx","sha256":"7993ac5b827042cd95041775b183fde2d112b5ba7d5c82098e778a44e473826d"},{"executable":false,"path":"src/cli/codex/send.tsx","sha256":"7dbe3c79d5dcfbd9fd18b8bc93ca83d2187f86288e2d8beb8a314010c8c82360"},{"executable":false,"path":"src/cli/codex/status.tsx","sha256":"b8c31001cb2892c1bfccf502f9776ee06998eac4fc0356f92936fa2c34f272d8"},{"executable":false,"path":"src/cli/codex/wait.tsx","sha256":"0a4cc6c440ad496682031658bda375fee29f5c346d833618d4f143de29b351cd"},{"executable":false,"path":"src/cli/codex/watch.tsx","sha256":"3280f42d15941cfd00b3727d9e271972c39165bd28db8f56acca5664d777cb4d"},{"executable":false,"path":"src/cli/doctor.tsx","sha256":"c30ce4e57fe707d36de29700abf5a2c56834bb4fb3adf2a70bb1089b4150ddc8"},{"executable":false,"path":"src/cli/groups/add.tsx","sha256":"2a5de115852f0ca6a6638300bd5006862a02a2f1e2f527b6fd680f610c1d777a"},{"executable":false,"path":"src/cli/groups/create.tsx","sha256":"da3f2539f338063daf51b80d86c27b2752f0e4a0334078d11508e27981590fa3"},{"executable":false,"path":"src/cli/groups/delete.tsx","sha256":"2d4e91113a2c24cefefcfd464513918791592b5e8734a4ef901eabe42c9c7289"},{"executable":false,"path":"src/cli/groups/get.tsx","sha256":"efed88fb3ab7292d60d6532f1f966f784ba3376b7826c9f3e5028467f86241e9"},{"executable":false,"path":"src/cli/groups/list.tsx","sha256":"f3cd229d41dd389b767447a04267700ab2293eae87a878f7d88149cb5798f4a6"},{"executable":false,"path":"src/cli/groups/members.tsx","sha256":"9b9eaecb65ea995006eff1b24a287110d16bf4830a0b0d07221c2f1317853641"},{"executable":false,"path":"src/cli/groups/remove.tsx","sha256":"96dd85d1b9732681ce9dc126ac3ae562d3f287f4c28e36b9a8f8c301dc93f63d"},{"executable":false,"path":"src/cli/groups/set.tsx","sha256":"4f9d90db612f14ddbb6d2afa5e45b66f044aed661ff95415e567e1be3b6108c6"},{"executable":false,"path":"src/cli/groups/update.tsx","sha256":"f44305815ddb6e27286f97ef68c04cfa5548acd5f04b1aba10ee7ce3cdbfc851"},{"executable":false,"path":"src/cli/history.tsx","sha256":"5151b48113b4ba0516bacabfabcd48e3256ead807c34d914deb84f31ded0b062"},{"executable":false,"path":"src/cli/send.tsx","sha256":"47297793ba98380300c45951d4d36e68b0f74e2933a5b6ac6634c4b6708f5ce7"},{"executable":false,"path":"src/cli/skills/add.tsx","sha256":"fbde237d172c026d7912174e705c03cbc9d74dad7806fc58c6ceb43127c69ba6"},{"executable":false,"path":"src/cli/skills/list.tsx","sha256":"34f255687c69aaf598a1dc54220453b57c46ed9ab4a168648d9c7eca6535ebae"},{"executable":false,"path":"src/cli/skills/remove.tsx","sha256":"21662c80a62f276c2dfd840a328e9665c0c5f63959a239d4dd877372c46f60d0"},{"executable":false,"path":"src/cli/thread.tsx","sha256":"29ab775936fa2987cb789754a44888fc52bdaad07180c5471122dda508cfd0c2"},{"executable":false,"path":"src/commands/codex-send.md","sha256":"1f88961a6656ae94a5cadd86e25eeaae111ee4c6aaad64f32917ac0c9d8410b8"},{"executable":false,"path":"src/commands/codex-threads.md","sha256":"e17b90cb8b0115a53161f83797ec55ca65e81600396cf3ea896b6306d2fb2dd2"},{"executable":false,"path":"src/commands/codex-wait.md","sha256":"acd0aaf00329256b874ec59666d2d803bbaf86b1d249d996a5f226b559619530"},{"executable":false,"path":"src/core/app-session.js","sha256":"2b2eb53ec3f4c7ea0dccf269fa9720af6f1ce36b8ca36e889d2bb271a092717d"},{"executable":false,"path":"src/core/chatgpt-desktop/adapter.ts","sha256":"5a0a4f2cea9f1fc1a739c534aed3979c28e0bd9c057b8475e6251974ad44ed7a"},{"executable":false,"path":"src/core/chatgpt-desktop/app-server-fallback.ts","sha256":"44002096eea29b5ea26d5b3d186b5616fcdab570888e3cb86fd4b5c5804af04c"},{"executable":false,"path":"src/core/chatgpt-desktop/cdp-adapter.ts","sha256":"db60fdd3be0259a6c76cad7d8ff86eddc6acbbd21c5f20d6aeffff5390063efa"},{"executable":false,"path":"src/core/chatgpt-desktop/cdp-dom.ts","sha256":"a122fb76626006fa8dedb7088f9b5533a8058c3a58838692b168e0753b7c89eb"},{"executable":false,"path":"src/core/chatgpt-desktop/cdp-session.ts","sha256":"8d6512ee80b179eedf54504110c1e88c5233b0b922667b00f87751452164cc00"},{"executable":false,"path":"src/core/chatgpt-desktop/errors.ts","sha256":"941ce4a9148fc53086cce64336dba473c1f7e4138774fd50a8ae524dcc05d34d"},{"executable":false,"path":"src/core/chatgpt-desktop/facade.ts","sha256":"bc9ac3bfd24db8458af6d20893fb3ab2ed701778f709aadbd141d61ed6c6c29b"},{"executable":false,"path":"src/core/chatgpt-desktop/index.ts","sha256":"591e6968c008cc75827da94b91c0ea15e573640876814e35e2470342dc8bccf9"},{"executable":false,"path":"src/core/chatgpt-desktop/loopback.ts","sha256":"b746681e5c45c67445e6620f850eff7c4627e13979747a7f686374c10a51f52f"},{"executable":false,"path":"src/core/chatgpt-desktop/payload-budget.ts","sha256":"89410474315b3769bad58d3ab61999e169410b7d1fdc20819efb4ee9c82fe1ac"},{"executable":false,"path":"src/core/chatgpt-desktop/project-state.ts","sha256":"b33e55b6d6abf15f3d7d1489b8b178d4e907ea83ed1a47d88f8f178a574709f8"},{"executable":false,"path":"src/core/chatgpt-desktop/relaunch.ts","sha256":"14851e115de7c45e256c4b9c3829dc4e796f1d989cf67d65115701a30b6d3a81"},{"executable":false,"path":"src/core/chatgpt-desktop/remote-threads.ts","sha256":"ed93418ff5d2bb51016783e7b0a458805e709b212974a66be804ddb9f7038033"},{"executable":false,"path":"src/core/chatgpt-desktop/routes.ts","sha256":"ccf8cde780da6e6e03f11e4dcc0d9d6bc4a6a3fd20d3210e74f0217e3660b7e1"},{"executable":false,"path":"src/core/chatgpt-desktop/thread-ids.ts","sha256":"0fe25e9d32c078bd9e7a171ab4bc4ee0b15190cd6d6c4d3b345926b3c608e082"},{"executable":false,"path":"src/core/chatgpt-desktop/types.ts","sha256":"59f89694eed34206e32db2a0afb48670bb164a4ffffacb907c8629bb0ce4bcca"},{"executable":false,"path":"src/core/claude-channel.js","sha256":"01ac7daef748a11bc99e132950ed7c59f50442e417eecf894b59cb4bb484202f"},{"executable":false,"path":"src/core/claude-routes.ts","sha256":"d34ddf25b8634ef86d46ab663f30a49aaedf87f8dc98e4036f84c1c88efa4083"},{"executable":false,"path":"src/core/codex-bridge.js","sha256":"cce4df66b5c8207ca5ac1aac1f04a07146ee8aa574063206736a4149de7cdc3a"},{"executable":false,"path":"src/core/codex/contract.js","sha256":"16bcbd0fc09844166c52694ebb1075e60b91296a8f04fb02ad061c30b0e9c591"},{"executable":false,"path":"src/core/codex/conversation.js","sha256":"05abb2253480d0793bf5f6b3db17c47e7d154973747074c7c76a79e6f61b7deb"},{"executable":false,"path":"src/core/codex/remote-control.js","sha256":"52565007799e98c3bff4297287d8e53005e285a95bab9e4e4d0a088bded4fa8e"},{"executable":false,"path":"src/core/codex/routes.ts","sha256":"4fd73b060ee7bc6ac00abe552b1e39baca5e9aba0bb1a095267148c00d0526cc"},{"executable":false,"path":"src/core/codex/thread-id.js","sha256":"4eb6c1929c2e7687ed6c1422789fa01c3e1c556ead7d16355eafbae46a7126f4"},{"executable":false,"path":"src/core/commands.js","sha256":"3ad8fafb86744a57992da93ad838ade0e9acb1b7ca09b829cefadbf2781be0c9"},{"executable":false,"path":"src/core/desktop-shim-bridge.js","sha256":"9d0176abafad9ba49adceb597c84111b5c165088ad5c0159a103b71f916cec64"},{"executable":false,"path":"src/core/desktop-shim.js","sha256":"fe644c893623123c5c95a5194f9346945524d2eb2d3c0734569a4def3ec881f5"},{"executable":false,"path":"src/core/format.js","sha256":"27e02ed2a71ccf51d4d088368865b4d8686bc2c22aae3cdc2b1ad91de51224f3"},{"executable":false,"path":"src/core/gateway.js","sha256":"3a4f838ee423600d29e05f58060dcaf81cdcea4ac93b36dd6108363ac24bc606"},{"executable":false,"path":"src/core/grok-approval-routes.ts","sha256":"a33daa4d05e0c2594c795bd7654c22f3e1003e4aaad41cd70034d03651dd01f9"},{"executable":false,"path":"src/core/grok-approvals.js","sha256":"6b651a65bdd17241dd5b7a01d94a318ece9356c1c9b965b0578e08250d74e8fe"},{"executable":false,"path":"src/core/headers.js","sha256":"e229f2826ebdfb52949dab500798c0fa8a5ff7c4e63de91a63103bf45d2ae3cc"},{"executable":false,"path":"src/core/history.js","sha256":"38ca8e8f8dcd2ad42952c424ea9f00be5972d8751e4c0ac87214171903d82d89"},{"executable":false,"path":"src/core/relay/codex.js","sha256":"ff6e8f3158d171911a35b829cd76e1f5fd8319f8143f727a4854448fc09edeec"},{"executable":false,"path":"src/core/relay/completion.js","sha256":"1a4bb1b434a9b4bf150ac27e32d0de8fa9d34df61639c55fa9f0f667d72176c5"},{"executable":false,"path":"src/core/relay/control.js","sha256":"3645540f9f799b8742e0d5782b6324454a1486a780f6cb97260b96ef62411771"},{"executable":false,"path":"src/core/relay/detail.js","sha256":"59a5faae60c762a8428b369d6f5574874130c26eb8c4a0a0ac0941a330620730"},{"executable":false,"path":"src/core/relay/engine.js","sha256":"bed4fab0e3791324d73e1594884e06427969816c86f3423dcb5e7a0a9a4a4c24"},{"executable":false,"path":"src/core/relay/intake.js","sha256":"b31146fe3c9edd2f09323a454687403666591b6c12483a55b07ac610d2e3610a"},{"executable":false,"path":"src/core/relay/interactions.js","sha256":"6b576ed3a5b6659bc17e384d26e40d6ccb41b118b3a91cf5c902e9758ccbc1b1"},{"executable":false,"path":"src/core/relay/managed.js","sha256":"dbe03ed964bd09739e580de73b7e9f90508062fa2e53b8244c6ca7f78fc7a60d"},{"executable":false,"path":"src/core/relay/ownership.js","sha256":"74805f04f6a8bc02b2951614c4814aa42f54aaa3abca027a2413ed5a54814caf"},{"executable":false,"path":"src/core/relay/profile.js","sha256":"3347d1d493d3470d82722468d31571464d8e11aa0b4deac5bda75f574078dfe5"},{"executable":false,"path":"src/core/relay/records.js","sha256":"d0378385eff9dbc4a732fa08998681d0a47783765d99232d70ffe6e100fb6421"},{"executable":false,"path":"src/core/relay/routes.ts","sha256":"c749c0e722002c6484ebb349fab07d4361322d5a57adf8d2f9e58a1ae1dd4c65"},{"executable":false,"path":"src/core/relay/state.js","sha256":"3f18c310da9bf121e3fc238e75540e337b6b7406817a18c662d8af1f697c5ce7"},{"executable":false,"path":"src/core/relay/worker.js","sha256":"4176ad91872f36f7ea6565d9f30d23d9d7a80ef27d7af975af760457132ba148"},{"executable":false,"path":"src/core/sender-prefix.js","sha256":"8c7f8f665b8f3a8739071e7a64cb248069ddcc9a01c1aedc9c64d1cd6cc14550"},{"executable":false,"path":"src/core/store.js","sha256":"caefeeebf686cecd37ef19585ef0a5d3acba9dbf1564fe128c3afe4de067305c"},{"executable":false,"path":"src/core/transcript.js","sha256":"0355f0978d2d126b90858e3f661130243fc4730ee81ba13080af1dfa39d8b081"},{"executable":false,"path":"src/core/url-policy.js","sha256":"62bf6d3c8af1f5840d5e2d682d8f2e52fcbfbee6695728b47ed31816f1736089"},{"executable":false,"path":"src/core/user-machine-guidance.js","sha256":"c8409ae9679246d8f907f1e0d16ec1c415d6697f2ce048fb64a599734486b9a6"},{"executable":false,"path":"src/gbot-install.ts","sha256":"e3c833d7662610e70e65232e541017229fb9a049eacee0bdf31e2c33c6ab7189"},{"executable":false,"path":"src/gbot.ts","sha256":"c89a37b764956ceacb2917a991a6d82d987ed8d8e453c8d553485bbd7f3edcc0"},{"executable":false,"path":"src/mcp/claude-channel.ts","sha256":"2d52e0d862a492026b6cb408e3177c994996e2b4292903da5bbf5bc191019913"},{"executable":false,"path":"src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx","sha256":"86334421686350ea044fb1160c19b6f5889425feb78fd608675feb8137492090"},{"executable":false,"path":"src/mcp/grok-bot/tools/chatgpt_desktop_list_threads.tsx","sha256":"5f24cfdace92522891dfbaed0bd90e74b7de6bb8d193e0ef3bd5582c05a06914"},{"executable":false,"path":"src/mcp/grok-bot/tools/chatgpt_desktop_open_thread.tsx","sha256":"b415d5bd303693649abae96b6b9d2920d29c797f5367ae8fbb2b62b635528e37"},{"executable":false,"path":"src/mcp/grok-bot/tools/chatgpt_desktop_read_thread.tsx","sha256":"6adc4318f4b6c90b1d3970046266197348f79757c0d52ffe991d6338eb1fed6c"},{"executable":false,"path":"src/mcp/grok-bot/tools/chatgpt_desktop_search_threads.tsx","sha256":"1d50db88cf7d06a74524c8937ed9045eaaf2560f6766d6c411b3bb55575d1d3f"},{"executable":false,"path":"src/mcp/grok-bot/tools/chatgpt_desktop_send.tsx","sha256":"9e1b93735b9a38216c7e9aac4c5b62bc2ac20b60e1979a19c4610f0208961af5"},{"executable":false,"path":"src/mcp/grok-bot/tools/chatgpt_desktop_status.tsx","sha256":"681e50ae6c7d3a04a76ad8eadf865ab9d793b5eb3c78f961a42d0902adacc306"},{"executable":false,"path":"src/mcp/grok-bot/tools/chatgpt_desktop_wait_reply.tsx","sha256":"2786b62ff2a1a4e68229199935ac63c15614a0f0bdbef74b2d06e0c405516272"},{"executable":false,"path":"src/mcp/grok-bot/tools/claude_send.tsx","sha256":"c02924049f370d9cb5ae459d6275c6cae2c9f4f1328cb5fa8e2f3e6d12a6d656"},{"executable":false,"path":"src/mcp/grok-bot/tools/codex_new.tsx","sha256":"556c8d6f5f2b07b20e9daae7aa8008ee3775582a4d034415fc86fecda0c63866"},{"executable":false,"path":"src/mcp/grok-bot/tools/codex_send.tsx","sha256":"e1c972255f4efd84d55e8163017e5675dd4f6315eef0b503ba5105d59142026b"},{"executable":false,"path":"src/mcp/grok-bot/tools/codex_threads.tsx","sha256":"c35cae75d1a46d098173cab6f1e401b51d695f40d4bc307afae9dd4a62a6bd1a"},{"executable":false,"path":"src/mcp/grok-bot/tools/codex_wait.tsx","sha256":"cb08b094860f3a09c937eb8339108015fe0de57d9f6c6886aea2028cd33ef207"},{"executable":false,"path":"src/mcp/grok-bot/tools/codex_watch.tsx","sha256":"1bc603d36ea7542468e7e3eadbb86072990282e09e209f13ebfc64389d207c49"},{"executable":false,"path":"src/mcp/grok-bot/tools/gbot_bridge_start.tsx","sha256":"c75c5c8a30ec43709a4d32baa8c3bc9796595381bb04fd2499ca30cd515c5a0b"},{"executable":false,"path":"src/mcp/grok-bot/tools/gbot_bridge_status.tsx","sha256":"486ce643b35cfa2eabf78f0deeabce7198adc66133669eec630e1d12206ad642"},{"executable":false,"path":"src/mcp/grok-bot/tools/gbot_bridge_stop.tsx","sha256":"371428a6e4b4e1312dd95d9681c29e765aec55e65d80d7c474535df838a43457"},{"executable":false,"path":"src/mcp/grok-bot/tools/gbot_codex_respond.tsx","sha256":"030e9c2081ed4ed40732b098c929022af3083f609f10934b35e1f428d33ae637"},{"executable":false,"path":"src/mcp/grok-bot/tools/gbot_grok_approvals.tsx","sha256":"17f164acdc48d6bc79a4c5044c7082760dd2a1646e5b56f6bff54c53fed96451"},{"executable":false,"path":"src/mcp/grok-bot/tools/gbot_grok_respond.tsx","sha256":"41964bb8cf0eb0f8493afa1dde16cd94c4f589d1a7e410577d1ed63ec4648542"},{"executable":false,"path":"src/mcp/grok-bot/tools/gbot_send.tsx","sha256":"2101d279a0e6bda2d57ba3da5366411df460a95692d4e64a1096880a89121d9a"},{"executable":false,"path":"src/mcp/grok-bot/tools/gbot_thread.tsx","sha256":"ef4aefec7243efaad18335e8508c7c17af0cd9b0f8a4b3693a7af9f666807fd6"},{"executable":false,"path":"src/rules/codex-claude-on-user-machines.mdc","sha256":"a50ec93d021a807e0a18bf9311cdf103bc0ae3947e4c6a0bd78cbb941e8e5b24"},{"executable":false,"path":"src/scripts/gbot-relay.ts","sha256":"6690e316189ed8ebba43ea67a495338c927e4c0c21a9f896b7b2fecc8c6eb656"},{"executable":false,"path":"src/skills/chatgpt-desktop/SKILL.md","sha256":"0fe2560037f761203a317bf8755b0476ce864b7b1b13708559448d300aa4635c"},{"executable":false,"path":"src/skills/codex-dot/SKILL.md","sha256":"7e910c3e9e74800c09c9643983cf51cd090ff6ba448800d2702968cd89c6a005"},{"executable":false,"path":"src/skills/talk-to-grok-bot/references/bridge-administration.md","sha256":"b43f91464cdb885831bbf945766e4d9644fea017049ebcde3241a2da5ff0fc81"},{"executable":false,"path":"src/skills/talk-to-grok-bot/SKILL.md","sha256":"33d526cf5cd5d458d5f5b80992f201918077fd0ca8c74b8e15fbe2dd78d435bb"},{"executable":false,"path":"test/app-session.test.js","sha256":"0756b691703dd8c84bf0823f2afab16c6b158398e4ce6be229db6f459a0bbdb1"},{"executable":false,"path":"test/claude-channel.test.js","sha256":"c63337698001b9739414203d70b6d54d0794ec5970bd90d327923f50ab2355dc"},{"executable":false,"path":"test/codex-bridge.test.js","sha256":"f885418d4d8aa6337202c839539cfa013a9c4471263bc9af2631883febc4a84b"},{"executable":false,"path":"test/codex-commands.test.js","sha256":"d07355741037270a82f9319ef615ace388585c44cff5586868330b6811847d2c"},{"executable":false,"path":"test/codex-contract.test.js","sha256":"926c7609679d8b6ac3b568d56ba5efa20df86d016a9031f085d19adbfb0e9064"},{"executable":false,"path":"test/codex-conversation.test.js","sha256":"789d0245a8d470860f7535c6ef3849313205097f6df517ab9946e47a789f8534"},{"executable":false,"path":"test/codex-session.test.js","sha256":"b39fe6afe0784b16e37b208089279dea01095d33e06ae26a1b60871796c7738a"},{"executable":false,"path":"test/codex-surfaces.test.js","sha256":"56d1b6550cc69d462a05e892cf82aa6a41495857b753215104e03207db514c81"},{"executable":false,"path":"test/connect-gateway.test.js","sha256":"5fabd87640b39e75c218300ca6be278268f248f3ff75e8cb72f9fade9eb0f7f3"},{"executable":false,"path":"test/desktop-shim-startup.test.js","sha256":"ebc4152cd235794365d56e32c47dab03b7198ddaa3759dd405fb39c7f6031e84"},{"executable":false,"path":"test/desktop-shim.test.js","sha256":"73137724806b9fed3fd07d7a005bc2bcaa4303d769373c63a9d62d08d96e5941"},{"executable":false,"path":"test/doctor.test.js","sha256":"9685885df9e7363d3e077d246b1a17803c8899e9613b4a4d52ee4b9275e20694"},{"executable":false,"path":"test/gateway-groups.test.js","sha256":"14e227453e3e28c7cb60f91dba55733834c023ce9a914e40e22c68c899512bdc"},{"executable":false,"path":"test/gateway-send.test.js","sha256":"d059166153dde975f457e4e8c32ea08b0fc857b58fd48eab7da1959638e4fb05"},{"executable":false,"path":"test/gateway-skills.test.js","sha256":"feed62a884d1b7d12e358a27d73fb5587bcf761d4867ab1755d8bc34a4380069"},{"executable":false,"path":"test/gateway.test.js","sha256":"e5a7acd7ae347c82ef1e2a6000562767168e56d6f9d86aca8da46141731d1309"},{"executable":false,"path":"test/grok-approvals.test.js","sha256":"5ad4c548060a08dbe57949ab5ae240383fdb1709cd3694a909f5e1354fee3f73"},{"executable":false,"path":"test/helpers/codex-server.js","sha256":"432b4570601e94cf08c4861133d328404a97066642edb7ec0dbc314411a4d848"},{"executable":false,"path":"test/history.test.js","sha256":"eac4811f1b12d427df66996f33dd46bc3cbe31fc9fa030b3f32c571d5379f8dd"},{"executable":false,"path":"test/host-tool-inventory.test.js","sha256":"fa44f15f46d0cae486c13be3fae09568f17f8581001ec50a61478bec03f4c28c"},{"executable":false,"path":"test/pr69-integration.test.js","sha256":"a5dc99c11e2d3fcf18233c595f9c28301e5939f4b3657125eb3f970df2598840"},{"executable":false,"path":"test/relay-auth-recovery.test.js","sha256":"399c064f550fe33e166187a51da527728a97946d213377c8fcc8231378f5bbab"},{"executable":false,"path":"test/relay-codex.test.js","sha256":"43cb313e748e187f798a8a0bce5a01cce9ba360a52985b05ecededee9ad10581"},{"executable":false,"path":"test/relay-completion.test.js","sha256":"a8cc2402f82ce4b458074eafd32c8fa18f7ced7d85fdb6b7fa68f7b18b6a8c2e"},{"executable":false,"path":"test/relay-engine.test.js","sha256":"f02d137b0ef953dc46a9c6367742f455d58e31b7e3432130ddcf2844c9684f16"},{"executable":false,"path":"test/relay-failure-detail.test.js","sha256":"4b514b25e8938d72ee13b29e56b54d4d42b0058cc5022065eb37f5a5c1063d5d"},{"executable":false,"path":"test/relay-gateway-lifecycle.test.js","sha256":"e16e7a34f7b6bb07965003e3018aa3a43ff192602db1c177bec9b6268fddb78b"},{"executable":false,"path":"test/relay-interactions.test.js","sha256":"83d70b1a813923fc6bae02b18d328107d0d04a6a761ab73b2051e3d114c7eaa5"},{"executable":false,"path":"test/relay-lifecycle.test.js","sha256":"e90130e1f609839bfd3f74c3edc333a22f37fe20dcde0146daa8d497133da4b7"},{"executable":false,"path":"test/relay-state.test.js","sha256":"c9143d5fa150b1ebcef9d515e161e25af3370b71eaf800a7b2bb1da1678b0bd3"},{"executable":false,"path":"test/relay-surfaces.test.js","sha256":"ba17ac4f1ffc02d4e06cd73eda143422a3d4d2f750630af9ed4e828dc9d7d2be"},{"executable":false,"path":"test/relay-worker.test.js","sha256":"6bd1bdaaaae59997132f289f822b6c4b8c9616c268fb2b45861e03f697c08197"},{"executable":false,"path":"test/release-config.test.js","sha256":"f3078277086d722f21d07e4134e10b89d2e849a94a0fa40f30025a5cb6fa6a15"},{"executable":false,"path":"test/store.test.js","sha256":"5d4ae4295287539ef4b7f98d0ca0cd42a931a3c7b25b092fbd2b8f1a08ca4bac"},{"executable":false,"path":"test/test-runner.test.js","sha256":"966ff3b67edb8e1f03b50120d4d820ab0f871121e9b359609e73faae8446454f"},{"executable":false,"path":"test/transcript.test.js","sha256":"28795d9cc8e42653ac430d18e17ff1e6bc2feb1b7935fe5c69b336d58e1793ae"},{"executable":false,"path":"test/url-policy.test.js","sha256":"dacfe322e0cbff178c0d67a5f76b6bd8d9fef14aa57c09c697eb69fa0019629c"},{"executable":false,"path":"tests/fixtures/codex-global-state-remote.json","sha256":"e59377d0f03cb24d57b55558e9c91dd363619a8a92566835f1ad49b639f99525"},{"executable":false,"path":"tests/route-unit/chatgpt-desktop-e2e-regressions.test.ts","sha256":"9cfc7f538899954970fe643c13571f03d417450c519525e91d382a0b5f0b086a"},{"executable":false,"path":"tests/route-unit/chatgpt-desktop-mcp-followup.test.ts","sha256":"4a2db11a642b8daef05285f096ce026ca5b9b6394c31c0966d7e5f8c1d49f4ca"},{"executable":false,"path":"tests/route-unit/chatgpt-desktop-review.test.ts","sha256":"208dfdea18ccc408a279666a521d2f70fdce9bdaf28f86bc5d053f7ad09a7dd2"},{"executable":false,"path":"tests/route-unit/chatgpt-desktop.test.ts","sha256":"5cf61e174b1d4a7c0f2209d6746bb1880a96fe550f7d2c4ea94e849dd7f21299"},{"executable":false,"path":"tests/route-unit/tools.test.ts","sha256":"aa3dc253ac54fbd2da06cdd29b0a5163079a045d4c7302233149781009137cd7"},{"executable":false,"path":"tsconfig.json","sha256":"fa2ced6d1721e8280a6aed615717cffcef25f6e2d16e15889b2be2a9df4fc226"}]},"provenance":[{"path":".agents/plugins/marketplace.json","sourceInputs":["agent-bundle.config.ts"]},{"path":".claude-plugin/marketplace.json","sourceInputs":["agent-bundle.config.ts","package.json"]},{"path":".claude-plugin/plugin.json","sourceInputs":["agent-bundle.config.ts","src/mcp/claude-channel.ts","src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx","src/skills/chatgpt-desktop/SKILL.md","src/skills/codex-dot/SKILL.md","src/skills/talk-to-grok-bot/SKILL.md"]},{"path":".codex-plugin/mcp.json","sourceInputs":["agent-bundle.config.ts","src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx"]},{"path":".codex-plugin/plugin.json","sourceInputs":["agent-bundle.config.ts","package.json","src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx","src/skills/chatgpt-desktop/SKILL.md","src/skills/codex-dot/SKILL.md","src/skills/talk-to-grok-bot/SKILL.md"]},{"path":".cursor-plugin/marketplace.json","sourceInputs":["agent-bundle.config.ts"]},{"path":".cursor-plugin/mcp.json","sourceInputs":["agent-bundle.config.ts","src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx"]},{"path":".cursor-plugin/plugin.json","sourceInputs":["agent-bundle.config.ts","package.json","src/commands/codex-send.md","src/commands/codex-threads.md","src/commands/codex-wait.md","src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx","src/rules/codex-claude-on-user-machines.mdc","src/skills/chatgpt-desktop/SKILL.md","src/skills/codex-dot/SKILL.md","src/skills/talk-to-grok-bot/SKILL.md"]},{"path":".mcp.json","sourceInputs":["agent-bundle.config.ts","src/mcp/claude-channel.ts","src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx"]},{"path":"agent-bundle.compile-evidence.json","sourceInputs":["agent-bundle.config.ts"]},{"path":"bin/gbot-flight.mjs","sourceInputs":["agent-bundle.config.ts","package.json","src/cli/_shared.ts","src/cli/approvals/list.tsx","src/cli/approvals/respond.tsx","src/cli/bots/create.tsx","src/cli/bots/delete.tsx","src/cli/bots/get.tsx","src/cli/bots/list.tsx","src/cli/bots/update.tsx","src/cli/chatgpt-desktop/hosts.tsx","src/cli/chatgpt-desktop/read.tsx","src/cli/chatgpt-desktop/search.tsx","src/cli/chatgpt-desktop/send.tsx","src/cli/chatgpt-desktop/status.tsx","src/cli/chatgpt-desktop/threads.tsx","src/cli/claude/send.tsx","src/cli/codex/bridge/respond.tsx","src/cli/codex/bridge/run.tsx","src/cli/codex/bridge/start.tsx","src/cli/codex/bridge/status.tsx","src/cli/codex/bridge/stop.tsx","src/cli/codex/desktop-shim.tsx","src/cli/codex/list-threads.tsx","src/cli/codex/new.tsx","src/cli/codex/queue.tsx","src/cli/codex/send.tsx","src/cli/codex/status.tsx","src/cli/codex/wait.tsx","src/cli/codex/watch.tsx","src/cli/doctor.tsx","src/cli/groups/add.tsx","src/cli/groups/create.tsx","src/cli/groups/delete.tsx","src/cli/groups/get.tsx","src/cli/groups/list.tsx","src/cli/groups/members.tsx","src/cli/groups/remove.tsx","src/cli/groups/set.tsx","src/cli/groups/update.tsx","src/cli/history.tsx","src/cli/send.tsx","src/cli/skills/add.tsx","src/cli/skills/list.tsx","src/cli/skills/remove.tsx","src/cli/thread.tsx","src/core/app-session.js","src/core/chatgpt-desktop/app-server-fallback.ts","src/core/chatgpt-desktop/cdp-adapter.ts","src/core/chatgpt-desktop/cdp-dom.ts","src/core/chatgpt-desktop/cdp-session.ts","src/core/chatgpt-desktop/errors.ts","src/core/chatgpt-desktop/facade.ts","src/core/chatgpt-desktop/loopback.ts","src/core/chatgpt-desktop/payload-budget.ts","src/core/chatgpt-desktop/project-state.ts","src/core/chatgpt-desktop/remote-threads.ts","src/core/chatgpt-desktop/routes.ts","src/core/chatgpt-desktop/thread-ids.ts","src/core/claude-channel.js","src/core/claude-routes.ts","src/core/codex-bridge.js","src/core/codex/contract.js","src/core/codex/conversation.js","src/core/codex/remote-control.js","src/core/codex/routes.ts","src/core/codex/thread-id.js","src/core/commands.js","src/core/desktop-shim-bridge.js","src/core/desktop-shim.js","src/core/format.js","src/core/gateway.js","src/core/grok-approval-routes.ts","src/core/grok-approvals.js","src/core/headers.js","src/core/history.js","src/core/relay/control.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/relay/routes.ts","src/core/sender-prefix.js","src/core/store.js","src/core/transcript.js","src/core/url-policy.js","src/core/user-machine-guidance.js","src/gbot.ts"]},{"path":"bin/gbot.mjs","sourceInputs":["agent-bundle.config.ts","package.json","src/cli/_shared.ts","src/cli/approvals/list.tsx","src/cli/approvals/respond.tsx","src/cli/bots/create.tsx","src/cli/bots/delete.tsx","src/cli/bots/get.tsx","src/cli/bots/list.tsx","src/cli/bots/update.tsx","src/cli/chatgpt-desktop/hosts.tsx","src/cli/chatgpt-desktop/read.tsx","src/cli/chatgpt-desktop/search.tsx","src/cli/chatgpt-desktop/send.tsx","src/cli/chatgpt-desktop/status.tsx","src/cli/chatgpt-desktop/threads.tsx","src/cli/claude/send.tsx","src/cli/codex/bridge/respond.tsx","src/cli/codex/bridge/run.tsx","src/cli/codex/bridge/start.tsx","src/cli/codex/bridge/status.tsx","src/cli/codex/bridge/stop.tsx","src/cli/codex/desktop-shim.tsx","src/cli/codex/list-threads.tsx","src/cli/codex/new.tsx","src/cli/codex/queue.tsx","src/cli/codex/send.tsx","src/cli/codex/status.tsx","src/cli/codex/wait.tsx","src/cli/codex/watch.tsx","src/cli/doctor.tsx","src/cli/groups/add.tsx","src/cli/groups/create.tsx","src/cli/groups/delete.tsx","src/cli/groups/get.tsx","src/cli/groups/list.tsx","src/cli/groups/members.tsx","src/cli/groups/remove.tsx","src/cli/groups/set.tsx","src/cli/groups/update.tsx","src/cli/history.tsx","src/cli/send.tsx","src/cli/skills/add.tsx","src/cli/skills/list.tsx","src/cli/skills/remove.tsx","src/cli/thread.tsx","src/core/app-session.js","src/core/chatgpt-desktop/app-server-fallback.ts","src/core/chatgpt-desktop/cdp-adapter.ts","src/core/chatgpt-desktop/cdp-dom.ts","src/core/chatgpt-desktop/cdp-session.ts","src/core/chatgpt-desktop/errors.ts","src/core/chatgpt-desktop/facade.ts","src/core/chatgpt-desktop/loopback.ts","src/core/chatgpt-desktop/payload-budget.ts","src/core/chatgpt-desktop/project-state.ts","src/core/chatgpt-desktop/remote-threads.ts","src/core/chatgpt-desktop/routes.ts","src/core/chatgpt-desktop/thread-ids.ts","src/core/claude-channel.js","src/core/claude-routes.ts","src/core/codex-bridge.js","src/core/codex/contract.js","src/core/codex/conversation.js","src/core/codex/remote-control.js","src/core/codex/routes.ts","src/core/codex/thread-id.js","src/core/commands.js","src/core/desktop-shim-bridge.js","src/core/desktop-shim.js","src/core/format.js","src/core/gateway.js","src/core/grok-approval-routes.ts","src/core/grok-approvals.js","src/core/headers.js","src/core/history.js","src/core/relay/control.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/relay/routes.ts","src/core/sender-prefix.js","src/core/store.js","src/core/transcript.js","src/core/url-policy.js","src/core/user-machine-guidance.js","src/gbot.ts"]},{"path":"commands/codex-send.md","sourceInputs":["src/commands/codex-send.md"]},{"path":"commands/codex-threads.md","sourceInputs":["src/commands/codex-threads.md"]},{"path":"commands/codex-wait.md","sourceInputs":["src/commands/codex-wait.md"]},{"path":"INSTALL.md","sourceInputs":["agent-bundle.config.ts"]},{"path":"install.mjs","sourceInputs":["agent-bundle.config.ts"]},{"path":"mcp.json","sourceInputs":["src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx"]},{"path":"mcp/mcp-claude-channel-8029413c.mjs","sourceInputs":["src/core/claude-channel.js","src/core/user-machine-guidance.js","src/mcp/claude-channel.ts"]},{"path":"mcp/mcp-grok-bot-b8c2461e-flight.mjs","sourceInputs":["package.json","src/core/app-session.js","src/core/chatgpt-desktop/app-server-fallback.ts","src/core/chatgpt-desktop/cdp-adapter.ts","src/core/chatgpt-desktop/cdp-dom.ts","src/core/chatgpt-desktop/cdp-session.ts","src/core/chatgpt-desktop/errors.ts","src/core/chatgpt-desktop/facade.ts","src/core/chatgpt-desktop/loopback.ts","src/core/chatgpt-desktop/payload-budget.ts","src/core/chatgpt-desktop/project-state.ts","src/core/chatgpt-desktop/remote-threads.ts","src/core/chatgpt-desktop/routes.ts","src/core/chatgpt-desktop/thread-ids.ts","src/core/claude-channel.js","src/core/claude-routes.ts","src/core/codex-bridge.js","src/core/codex/contract.js","src/core/codex/conversation.js","src/core/codex/remote-control.js","src/core/codex/routes.ts","src/core/codex/thread-id.js","src/core/desktop-shim-bridge.js","src/core/desktop-shim.js","src/core/format.js","src/core/gateway.js","src/core/grok-approval-routes.ts","src/core/grok-approvals.js","src/core/headers.js","src/core/relay/control.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/relay/routes.ts","src/core/sender-prefix.js","src/core/store.js","src/core/transcript.js","src/core/url-policy.js","src/core/user-machine-guidance.js","src/gbot.ts","src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_list_threads.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_open_thread.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_read_thread.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_search_threads.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_send.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_status.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_wait_reply.tsx","src/mcp/grok-bot/tools/claude_send.tsx","src/mcp/grok-bot/tools/codex_new.tsx","src/mcp/grok-bot/tools/codex_send.tsx","src/mcp/grok-bot/tools/codex_threads.tsx","src/mcp/grok-bot/tools/codex_wait.tsx","src/mcp/grok-bot/tools/codex_watch.tsx","src/mcp/grok-bot/tools/gbot_bridge_start.tsx","src/mcp/grok-bot/tools/gbot_bridge_status.tsx","src/mcp/grok-bot/tools/gbot_bridge_stop.tsx","src/mcp/grok-bot/tools/gbot_codex_respond.tsx","src/mcp/grok-bot/tools/gbot_grok_approvals.tsx","src/mcp/grok-bot/tools/gbot_grok_respond.tsx","src/mcp/grok-bot/tools/gbot_send.tsx","src/mcp/grok-bot/tools/gbot_thread.tsx"]},{"path":"mcp/mcp-grok-bot-b8c2461e.mjs","sourceInputs":["package.json","src/core/app-session.js","src/core/chatgpt-desktop/app-server-fallback.ts","src/core/chatgpt-desktop/cdp-adapter.ts","src/core/chatgpt-desktop/cdp-dom.ts","src/core/chatgpt-desktop/cdp-session.ts","src/core/chatgpt-desktop/errors.ts","src/core/chatgpt-desktop/facade.ts","src/core/chatgpt-desktop/loopback.ts","src/core/chatgpt-desktop/payload-budget.ts","src/core/chatgpt-desktop/project-state.ts","src/core/chatgpt-desktop/remote-threads.ts","src/core/chatgpt-desktop/routes.ts","src/core/chatgpt-desktop/thread-ids.ts","src/core/claude-channel.js","src/core/claude-routes.ts","src/core/codex-bridge.js","src/core/codex/contract.js","src/core/codex/conversation.js","src/core/codex/remote-control.js","src/core/codex/routes.ts","src/core/codex/thread-id.js","src/core/desktop-shim-bridge.js","src/core/desktop-shim.js","src/core/format.js","src/core/gateway.js","src/core/grok-approval-routes.ts","src/core/grok-approvals.js","src/core/headers.js","src/core/relay/control.js","src/core/relay/managed.js","src/core/relay/profile.js","src/core/relay/routes.ts","src/core/sender-prefix.js","src/core/store.js","src/core/transcript.js","src/core/url-policy.js","src/core/user-machine-guidance.js","src/gbot.ts","src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_list_threads.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_open_thread.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_read_thread.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_search_threads.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_send.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_status.tsx","src/mcp/grok-bot/tools/chatgpt_desktop_wait_reply.tsx","src/mcp/grok-bot/tools/claude_send.tsx","src/mcp/grok-bot/tools/codex_new.tsx","src/mcp/grok-bot/tools/codex_send.tsx","src/mcp/grok-bot/tools/codex_threads.tsx","src/mcp/grok-bot/tools/codex_wait.tsx","src/mcp/grok-bot/tools/codex_watch.tsx","src/mcp/grok-bot/tools/gbot_bridge_start.tsx","src/mcp/grok-bot/tools/gbot_bridge_status.tsx","src/mcp/grok-bot/tools/gbot_bridge_stop.tsx","src/mcp/grok-bot/tools/gbot_codex_respond.tsx","src/mcp/grok-bot/tools/gbot_grok_approvals.tsx","src/mcp/grok-bot/tools/gbot_grok_respond.tsx","src/mcp/grok-bot/tools/gbot_send.tsx","src/mcp/grok-bot/tools/gbot_thread.tsx"]},{"path":"plugin.json","sourceInputs":["agent-bundle.config.ts","package.json"]},{"path":"rules/codex-claude-on-user-machines.mdc","sourceInputs":["src/rules/codex-claude-on-user-machines.mdc"]},{"path":"scripts/gbot-relay.mjs","sourceInputs":["package.json","src/core/app-session.js","src/core/codex-bridge.js","src/core/codex/contract.js","src/core/codex/conversation.js","src/core/codex/remote-control.js","src/core/codex/thread-id.js","src/core/desktop-shim-bridge.js","src/core/desktop-shim.js","src/core/gateway.js","src/core/grok-approvals.js","src/core/headers.js","src/core/relay/codex.js","src/core/relay/completion.js","src/core/relay/control.js","src/core/relay/detail.js","src/core/relay/engine.js","src/core/relay/intake.js","src/core/relay/interactions.js","src/core/relay/ownership.js","src/core/relay/profile.js","src/core/relay/records.js","src/core/relay/state.js","src/core/relay/worker.js","src/core/sender-prefix.js","src/core/store.js","src/core/transcript.js","src/core/url-policy.js","src/core/user-machine-guidance.js","src/scripts/gbot-relay.ts"]},{"path":"skills/chatgpt-desktop/SKILL.md","sourceInputs":["src/skills/chatgpt-desktop/SKILL.md"]},{"path":"skills/codex-dot/SKILL.md","sourceInputs":["src/skills/codex-dot/SKILL.md"]},{"path":"skills/talk-to-grok-bot/references/bridge-administration.md","sourceInputs":["src/skills/talk-to-grok-bot/references/bridge-administration.md","src/skills/talk-to-grok-bot/SKILL.md"]},{"path":"skills/talk-to-grok-bot/SKILL.md","sourceInputs":["src/skills/talk-to-grok-bot/SKILL.md"]}],"recordVersion":1,"validation":{"artifact":{"status":"passed"},"projections":[{"host":"claude","status":"passed"},{"host":"codex","status":"passed"},{"host":"cursor","status":"passed"},{"host":"portable","status":"passed"}],"source":{"status":"passed"}}},"distribution":{"channels":["local","npm"],"install":{"instructions":"INSTALL.md","script":"install.mjs"},"payloads":[]},"executables":{"bins":[{"hosts":["claude","codex","cursor","portable"],"name":"gbot","path":"bin/gbot.mjs","worker":"bin/gbot-flight.mjs"}],"hooks":[],"mcpServers":[{"apps":[],"hosts":["claude"],"id":"mcp:claude-channel","kind":"compiled","launch":{"args":[],"entry":"mcp/mcp-claude-channel-8029413c.mjs","env":{}},"name":"claude-channel","transport":"stdio"},{"apps":[],"hosts":["claude","codex","cursor","portable"],"id":"mcp:grok-bot","kind":"compiled","launch":{"args":[],"entry":"mcp/mcp-grok-bot-b8c2461e.mjs","env":{},"worker":"mcp/mcp-grok-bot-b8c2461e-flight.mjs"},"name":"grok-bot","transport":"stdio"}],"scripts":[{"hosts":["claude","codex","cursor","portable"],"id":"script:gbot-relay","mode":"bundle","name":"gbot-relay","path":"scripts/gbot-relay.mjs"}]},"files":[{"bytes":225,"kind":"generated","path":".agents/plugins/marketplace.json","sha256":"54d575719e003eb71ba2d7fb7379e12098243e701646e0ca83dba38ef2a78b1d"},{"bytes":926,"kind":"generated","path":".claude-plugin/marketplace.json","sha256":"7335705d34b85380cfaf94d2b43052d5753b54ab0291bed540cbe8d1a6ef7cac"},{"bytes":339,"kind":"generated","path":".claude-plugin/plugin.json","sha256":"bc4a32f7e5ff0d8839826a9d46816f3fc98c6706de3e3a9b06526814a564d873"},{"bytes":156,"kind":"generated","path":".codex-plugin/mcp.json","sha256":"4b442017d0d30ec207cef7a4e5d2cd9dca246080008633459a27e3a5f2f7935b"},{"bytes":1310,"kind":"generated","path":".codex-plugin/plugin.json","sha256":"5a048e6ec9e77b62c3fb26b082c12ba206cc4b066686c71425f3481c8c2e5a33"},{"bytes":332,"kind":"generated","path":".cursor-plugin/marketplace.json","sha256":"297ba8aebcdb8bcac814b520bf21d9d6acc9c8c94a1ecf3ca442a49d834aab59"},{"bytes":169,"kind":"generated","path":".cursor-plugin/mcp.json","sha256":"95c87d0f2ed3a4f6df7d0e519454b1234672eca030db3b8cd8776ef0f35dee74"},{"bytes":694,"kind":"generated","path":".cursor-plugin/plugin.json","sha256":"3ad2c4641736bf6ff91af4d8f5e9a1b4297aa8134e387d007154656eaa87ac5e"},{"bytes":363,"kind":"generated","path":".mcp.json","sha256":"5c56ac183c3d4fcbfa9386a73ab805495c6ab613b96bf71dbd5be80093e1a10b"},{"bytes":22732,"kind":"generated","path":"agent-bundle.compile-evidence.json","sha256":"21379c8c67a5e6e59da3306ad5879a9662bf3f476fba7aaea1ccbe64a8c60a90"},{"bytes":1609294,"kind":"bundle","path":"bin/gbot-flight.mjs","sha256":"09eb472cda4e154e2216b30f8e3f9d3ecbb349203320138d22a39354bf0ae42e"},{"bytes":2483958,"kind":"bundle","mode":493,"path":"bin/gbot.mjs","sha256":"89ad2b22378d67bb85a9fa9f6e20ff4fe4ddd28d90f91827ca7839c2937198f1"},{"bytes":375,"kind":"generated","path":"commands/codex-send.md","sha256":"622af5d2945c3dc9fe3035b4e35a63cd6041e0b7a9570a5f5648ba857eae9074"},{"bytes":250,"kind":"generated","path":"commands/codex-threads.md","sha256":"a763a6e1d378d012cdf6f52bd5962cf8b3a0806ef5be88472ea199d250cf6a4b"},{"bytes":267,"kind":"generated","path":"commands/codex-wait.md","sha256":"f203f7e33ad2dd59723b1117fb65a5de062a16f8b7edda1b034f02e635c96f9a"},{"bytes":22762,"kind":"generated","path":"INSTALL.md","sha256":"f7b47cb4c9752966736551b712056dedc58c8405975451c151bd549e4625bd11"},{"bytes":78609,"kind":"generated","path":"install.mjs","sha256":"1f073590820c6ddc2ec33cb1c6d2aaed3dbdd00b319621bd7bfcac40a1bc1a2e"},{"bytes":246,"kind":"generated","path":"mcp.json","sha256":"c06a01fa851792f42e0cc807a444e454c3ea2fd164dc1c2adef455acc93879e2"},{"bytes":2275669,"kind":"bundle","path":"mcp/mcp-claude-channel-8029413c.mjs","sha256":"46c0e5abb42f0dcd1152ec9d6bf5fed42c3bcb109f89312bfaa9870ecc059fe3"},{"bytes":1550730,"kind":"bundle","path":"mcp/mcp-grok-bot-b8c2461e-flight.mjs","sha256":"f2c4934e59fe73c8d7bd586c2c01adbabec0c7872b710134681e88532fdd128e"},{"bytes":3378575,"kind":"bundle","path":"mcp/mcp-grok-bot-b8c2461e.mjs","sha256":"8d81d90e969e782a30e4571e0383d4460cf8d6a44616e0176fc13699cd50cb61"},{"bytes":681,"kind":"generated","path":"plugin.json","sha256":"a502feae7220144ce4ce55c18dea8926a062e5d902da897bc6fd62a6b247a90a"},{"bytes":953,"kind":"generated","path":"rules/codex-claude-on-user-machines.mdc","sha256":"a50ec93d021a807e0a18bf9311cdf103bc0ae3947e4c6a0bd78cbb941e8e5b24"},{"bytes":1317866,"kind":"bundle","path":"scripts/gbot-relay.mjs","sha256":"360b4c348d3252054a396d9e903a4c6e277992a0f3bffe7cd580016816f585cd"},{"bytes":4856,"kind":"copy","path":"skills/chatgpt-desktop/SKILL.md","sha256":"0fe2560037f761203a317bf8755b0476ce864b7b1b13708559448d300aa4635c"},{"bytes":2134,"kind":"copy","path":"skills/codex-dot/SKILL.md","sha256":"7e910c3e9e74800c09c9643983cf51cd090ff6ba448800d2702968cd89c6a005"},{"bytes":6620,"kind":"copy","path":"skills/talk-to-grok-bot/references/bridge-administration.md","sha256":"b43f91464cdb885831bbf945766e4d9644fea017049ebcde3241a2da5ff0fc81"},{"bytes":5679,"kind":"copy","path":"skills/talk-to-grok-bot/SKILL.md","sha256":"33d526cf5cd5d458d5f5b80992f201918077fd0ca8c74b8e15fbe2dd78d435bb"}],"manifestVersion":6,"projections":[{"builtInHost":"claude","documents":{"marketplace":".claude-plugin/marketplace.json","mcp":".mcp.json","plugin":".claude-plugin/plugin.json"},"host":"claude","marketplace":{"name":"gbot-marketplace"}},{"builtInHost":"codex","documents":{"marketplace":".agents/plugins/marketplace.json","mcp":".codex-plugin/mcp.json","plugin":".codex-plugin/plugin.json"},"host":"codex","marketplace":{"name":"gbot-marketplace"}},{"builtInHost":"cursor","documents":{"marketplace":".cursor-plugin/marketplace.json","mcp":".cursor-plugin/mcp.json","plugin":".cursor-plugin/plugin.json"},"host":"cursor","marketplace":{"name":"gbot-marketplace"}},{"builtInHost":"portable","documents":{"mcp":"mcp.json","plugin":"plugin.json"},"host":"portable"}],"routes":{"cli":{"commands":[{"aliases":[],"description":"List current Grok approval cards (latest 200 entries).","exitCode":"zero","options":[{"key":"target","kind":"string","option":"target","positional":0,"repeated":false,"required":true}],"path":["approvals","list"],"routeId":"cli:approvals/list"},{"aliases":[],"description":"Explicit user decision: accept a Grok request once or decline; never grant persistent permissions.","exitCode":"zero","options":[{"choices":["accept","decline"],"key":"decision","kind":"enum","option":"decision","repeated":false,"required":true},{"key":"entryId","kind":"string","option":"entry-id","repeated":false,"required":true},{"key":"requestId","kind":"string","option":"request-id","repeated":false,"required":true},{"key":"target","kind":"string","option":"target","repeated":false,"required":true}],"path":["approvals","respond"],"routeId":"cli:approvals/respond"},{"aliases":[],"description":"Create a Grok Bot bot.","exitCode":"zero","options":[{"key":"avatarColor","kind":"string","option":"avatar-color","repeated":false,"required":false},{"key":"avatarShape","kind":"string","option":"avatar-shape","repeated":false,"required":false},{"key":"description","kind":"string","option":"description","repeated":false,"required":false},{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"key":"name","kind":"string","option":"name","repeated":false,"required":true},{"key":"title","kind":"string","option":"title","repeated":false,"required":false}],"path":["bots","create"],"routeId":"cli:bots/create"},{"aliases":[],"description":"Delete a bot or group by id or name.","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"description":"Bot or group id or name","key":"ref","kind":"string","option":"ref","positional":0,"repeated":false,"required":true}],"path":["bots","delete"],"routeId":"cli:bots/delete"},{"aliases":[],"description":"Show one bot or group by id or name.","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"description":"Bot or group id or name","key":"ref","kind":"string","option":"ref","positional":0,"repeated":false,"required":true}],"path":["bots","get"],"routeId":"cli:bots/get"},{"aliases":[],"description":"List Grok Bot bots (not groups).","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false}],"path":["bots","list"],"routeId":"cli:bots/list"},{"aliases":[],"description":"Update a bot or group profile.","exitCode":"zero","options":[{"key":"avatarColor","kind":"string","option":"avatar-color","repeated":false,"required":false},{"key":"avatarShape","kind":"string","option":"avatar-shape","repeated":false,"required":false},{"key":"description","kind":"string","option":"description","repeated":false,"required":false},{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"key":"hidden","kind":"string","option":"hidden","repeated":false,"required":false},{"key":"name","kind":"string","option":"name","repeated":false,"required":false},{"key":"notify","kind":"string","option":"notify","repeated":false,"required":false},{"description":"Bot or group id or name","key":"ref","kind":"string","option":"ref","positional":0,"repeated":false,"required":true},{"key":"title","kind":"string","option":"title","repeated":false,"required":false}],"path":["bots","update"],"routeId":"cli:bots/update"},{"aliases":[],"description":"Discover ChatGPT Desktop hosts and modelProviders at runtime (never hardcode). Always includes local plus every remote-thread-summaries-v3:; Providers come from distinct thread/list and remote-summary values.","exitCode":"result","options":[{"description":"Local CDP port","key":"port","kind":"number","option":"port","repeated":false,"required":false}],"path":["chatgpt-desktop","hosts"],"routeId":"cli:chatgpt-desktop/hosts"},{"aliases":[],"description":"Read a ChatGPT Desktop thread via app-server (turns/list); CDP DOM is fallback only.","exitCode":"result","options":[{"description":"Opaque nextCursor from the prior read page","key":"cursor","kind":"string","option":"cursor","repeated":false,"required":false},{"key":"full","kind":"boolean","option":"full","repeated":false,"required":false},{"description":"Recent default 100; full default 2000","key":"limit","kind":"number","option":"limit","repeated":false,"required":false},{"key":"openTimeoutMs","kind":"number","option":"open-timeout-ms","repeated":false,"required":false},{"key":"port","kind":"number","option":"port","repeated":false,"required":false},{"key":"threadId","kind":"string","option":"thread-id","positional":0,"repeated":false,"required":true}],"path":["chatgpt-desktop","read"],"routeId":"cli:chatgpt-desktop/read"},{"aliases":[],"description":"Search ChatGPT Desktop threads. --host and --model-provider accept any string (see gbot chatgpt-desktop hosts); not enums.","exitCode":"result","options":[{"description":"Opaque nextCursor from a prior search page","key":"cursor","kind":"string","option":"cursor","repeated":false,"required":false},{"choices":["host"],"description":"Group results by host (grouping mode, not a host id)","key":"groupBy","kind":"enum","option":"group-by","repeated":false,"required":false},{"description":"Any string: all (default), local, or a hostId/friendly name from `gbot chatgpt-desktop hosts`. Not an enum.","key":"host","kind":"string","option":"host","repeated":false,"required":false},{"description":"Max matches (1-200)","key":"limit","kind":"number","option":"limit","repeated":false,"required":false},{"description":"Any modelProvider id (app-server modelProviders pass-through). Omit = all. See `gbot chatgpt-desktop hosts`. Not an enum.","key":"modelProvider","kind":"string","option":"model-provider","repeated":false,"required":false},{"description":"Local CDP port","key":"port","kind":"number","option":"port","repeated":false,"required":false},{"description":"Project label or projectId filter.","key":"project","kind":"string","option":"project","repeated":false,"required":false},{"key":"query","kind":"string","option":"query","positional":0,"repeated":false,"required":true}],"path":["chatgpt-desktop","search"],"routeId":"cli:chatgpt-desktop/search"},{"aliases":[],"description":"Send to ChatGPT Desktop via CDP composer. Omit --thread-id for a new chat; --project prefers Start new chat in .","exitCode":"result","options":[{"key":"openTimeoutMs","kind":"number","option":"open-timeout-ms","repeated":false,"required":false},{"key":"port","kind":"number","option":"port","repeated":false,"required":false},{"key":"project","kind":"string","option":"project","repeated":false,"required":false},{"key":"text","kind":"string","option":"text","positional":0,"repeated":true,"required":true},{"key":"threadId","kind":"string","option":"thread-id","repeated":false,"required":false},{"description":"Explicitly unarchive an existing archived thread before sending.","key":"unarchive","kind":"boolean","option":"unarchive","repeated":false,"required":false}],"path":["chatgpt-desktop","send"],"routeId":"cli:chatgpt-desktop/send"},{"aliases":[],"description":"Probe local ChatGPT Desktop CDP on 127.0.0.1 and report app-server availability for list/deep-read.","exitCode":"result","options":[{"description":"Local CDP port (default 9222)","key":"port","kind":"number","option":"port","repeated":false,"required":false}],"path":["chatgpt-desktop","status"],"routeId":"cli:chatgpt-desktop/status"},{"aliases":[],"description":"List local + remote-control ChatGPT Desktop threads. --host and --model-provider accept any string (see gbot chatgpt-desktop hosts); not enums.","exitCode":"result","options":[{"description":"Opaque nextCursor from a previous page","key":"cursor","kind":"string","option":"cursor","repeated":false,"required":false},{"choices":["host"],"description":"Group results by host (grouping mode, not a host id)","key":"groupBy","kind":"enum","option":"group-by","repeated":false,"required":false},{"description":"Any string: all (default), local, or a hostId/friendly name from `gbot chatgpt-desktop hosts`. Not an enum.","key":"host","kind":"string","option":"host","repeated":false,"required":false},{"description":"Max threads (1-200)","key":"limit","kind":"number","option":"limit","repeated":false,"required":false},{"description":"Any modelProvider id (app-server modelProviders pass-through). Omit = all. See `gbot chatgpt-desktop hosts`. Not an enum.","key":"modelProvider","kind":"string","option":"model-provider","repeated":false,"required":false},{"description":"Local CDP port","key":"port","kind":"number","option":"port","repeated":false,"required":false},{"description":"Project label or projectId filter.","key":"project","kind":"string","option":"project","repeated":false,"required":false}],"path":["chatgpt-desktop","threads"],"routeId":"cli:chatgpt-desktop/threads"},{"aliases":[],"description":"Send to an explicitly enabled live Claude Code channel and wait for its reply.","exitCode":"result","options":[{"key":"message","kind":"string","option":"message","positional":1,"repeated":false,"required":true},{"key":"name","kind":"string","option":"name","positional":0,"repeated":false,"required":true},{"key":"timeoutMs","kind":"number","option":"timeout-ms","repeated":false,"required":false}],"path":["claude","send"],"routeId":"cli:claude/send"},{"aliases":[],"description":"Managed Grok/Codex bridge respond.","exitCode":"result","options":[{"description":"JSON object mapping each advertised question ID to {\"answers\":[\"answer\"]}; exact IDs required.","key":"answersJson","kind":"string","option":"answers-json","repeated":false,"required":false},{"key":"bindingId","kind":"string","option":"binding-id","repeated":false,"required":false},{"choices":["accept","decline","cancel"],"key":"decision","kind":"enum","option":"decision","repeated":false,"required":false},{"key":"exchangeId","kind":"string","option":"exchange-id","repeated":false,"required":false},{"key":"generation","kind":"string","option":"generation","repeated":false,"required":true},{"key":"interactionId","kind":"string","option":"interaction-id","repeated":false,"required":true},{"key":"threadId","kind":"string","option":"thread-id","repeated":false,"required":true},{"key":"turnId","kind":"string","option":"turn-id","repeated":false,"required":true}],"path":["codex","bridge","respond"],"routeId":"cli:codex/bridge/respond"},{"aliases":[],"description":"Run a bounded foreground relay (up to 23 hours). Use the packaged gbot-relay.mjs script for unlimited service lifetime.","exitCode":"result","options":[{"description":"Foreground lifetime in milliseconds (100..82800000; default 23h).","key":"lifetimeMs","kind":"number","option":"lifetime-ms","repeated":false,"required":false}],"path":["codex","bridge","run"],"routeId":"cli:codex/bridge/run"},{"aliases":[],"description":"Managed Grok/Codex bridge start.","exitCode":"result","options":[{"choices":["steer","reject"],"key":"busyPolicy","kind":"enum","option":"busy-policy","repeated":false,"required":false},{"key":"codexThreadId","kind":"string","option":"codex-thread-id","repeated":false,"required":false},{"key":"expectedCwd","kind":"string","option":"expected-cwd","repeated":false,"required":false},{"key":"grokTarget","kind":"string","option":"grok-target","positional":0,"repeated":false,"required":true},{"key":"requestId","kind":"string","option":"request-id","repeated":false,"required":false}],"path":["codex","bridge","start"],"routeId":"cli:codex/bridge/start"},{"aliases":[],"description":"Managed Grok/Codex bridge status.","exitCode":"result","options":[{"key":"bindingId","kind":"string","option":"binding-id","repeated":false,"required":false},{"key":"limit","kind":"number","option":"limit","repeated":false,"required":false}],"path":["codex","bridge","status"],"routeId":"cli:codex/bridge/status"},{"aliases":[],"description":"Managed Grok/Codex bridge stop.","exitCode":"result","options":[{"key":"all","kind":"boolean","option":"all","repeated":false,"required":false},{"key":"bindingId","kind":"string","option":"binding-id","repeated":false,"required":false},{"key":"worker","kind":"boolean","option":"worker","repeated":false,"required":false}],"path":["codex","bridge","stop"],"routeId":"cli:codex/bridge/stop"},{"aliases":[],"description":"Install, remove, or check the ChatGPT Desktop shim: a CODEX_CLI_PATH wrapper that bridges Desktop stdio onto the managed Codex daemon (stock app-server proxy hangs). macOS persists via LaunchAgent; always fails open to real Codex.","exitCode":"result","options":[{"choices":["install","uninstall","status"],"description":"install | uninstall | status","key":"action","kind":"enum","option":"action","positional":0,"repeated":false,"required":true}],"path":["codex","desktop-shim"],"routeId":"cli:codex/desktop-shim"},{"aliases":[],"description":"List Codex daemon-managed threads, newest activity first (all modelProviders; pages via --cursor / nextCursor). Find old threads with --query (name/title/id prefix, all pages), --active-within 7d, --since, --sort created.","exitCode":"result","options":[{"description":"Only threads updated within 90s, 30m, 12h, 7d, 2w","key":"activeWithin","kind":"string","option":"active-within","repeated":false,"required":false},{"description":"Archived threads only","key":"archived","kind":"boolean","option":"archived","repeated":false,"required":false},{"description":"Opaque pagination cursor from a previous nextCursor (may start with -)","key":"cursor","kind":"string","option":"cursor","repeated":false,"required":false},{"description":"Exact session cwd","key":"cwd","kind":"string","option":"cwd","repeated":false,"required":false},{"description":"Max threads to list (1-200)","key":"limit","kind":"number","option":"limit","repeated":false,"required":false},{"description":"Any modelProvider id; omit for all","key":"modelProvider","kind":"string","option":"model-provider","repeated":false,"required":false},{"choices":["desc","asc"],"key":"order","kind":"enum","option":"order","repeated":false,"required":false},{"description":"Case-insensitive substring of name/title, preview or id (id prefix works); scans every page","key":"query","kind":"string","option":"query","repeated":false,"required":false},{"description":"Only threads updated at/after this ISO date/time or epoch seconds","key":"since","kind":"string","option":"since","repeated":false,"required":false},{"choices":["updated","created","recency"],"description":"updated (last activity, default) | created | recency","key":"sort","kind":"enum","option":"sort","repeated":false,"required":false},{"description":"Any app-server source kind; omit for interactive sources","key":"sourceKind","kind":"string","option":"source-kind","repeated":false,"required":false}],"path":["codex","list-threads"],"routeId":"cli:codex/list-threads"},{"aliases":[],"description":"Start a new Codex thread in --cwd, optionally with --model, --effort and a message.","exitCode":"result","options":[{"key":"cwd","kind":"string","option":"cwd","repeated":false,"required":true},{"choices":["none","minimal","low","medium","high","xhigh"],"key":"effort","kind":"enum","option":"effort","repeated":false,"required":false},{"key":"expectedCwd","kind":"string","option":"expected-cwd","repeated":false,"required":false},{"key":"message","kind":"string","option":"message","positional":0,"repeated":true,"required":false},{"key":"model","kind":"string","option":"model","repeated":false,"required":false}],"path":["codex","new"],"routeId":"cli:codex/new"},{"aliases":[],"description":"List the experimental Codex thread queue (GROK_BOT_CODEX_EXPERIMENTAL=1).","exitCode":"result","options":[{"key":"threadId","kind":"string","option":"thread-id","positional":0,"repeated":false,"required":true}],"path":["codex","queue"],"routeId":"cli:codex/queue"},{"aliases":[],"description":"Send to Codex; acceptance is distinct from completion. Options precede threadId.","exitCode":"result","options":[{"key":"bindingId","kind":"string","option":"binding-id","repeated":false,"required":false},{"key":"correlationId","kind":"string","option":"correlation-id","repeated":false,"required":false},{"choices":["none","minimal","low","medium","high","xhigh"],"key":"effort","kind":"enum","option":"effort","repeated":false,"required":false},{"key":"envelope","kind":"boolean","option":"envelope","repeated":false,"required":false},{"key":"expectedCwd","kind":"string","option":"expected-cwd","repeated":false,"required":false},{"description":"Required active-turn guard for steer; stale guards reject.","key":"expectedTurnId","kind":"string","option":"expected-turn-id","repeated":false,"required":false},{"key":"hop","kind":"number","option":"hop","repeated":false,"required":false},{"description":"Reply budget: 1-4194304 bytes.","key":"maxOutputBytes","kind":"number","option":"max-output-bytes","repeated":false,"required":false},{"key":"message","kind":"string","option":"message","positional":1,"repeated":true,"required":true},{"key":"model","kind":"string","option":"model","repeated":false,"required":false},{"key":"replyTo","kind":"string","option":"reply-to","repeated":false,"required":false},{"key":"replyToGrok","kind":"string","option":"reply-to-grok","repeated":false,"required":false},{"key":"requestId","kind":"string","option":"request-id","repeated":false,"required":false},{"key":"threadId","kind":"string","option":"thread-id","positional":0,"repeated":false,"required":true},{"description":"Observation timeout: 1-7200000 milliseconds.","key":"timeoutMs","kind":"number","option":"timeout-ms","repeated":false,"required":false},{"key":"wait","kind":"boolean","option":"wait","repeated":false,"required":false},{"choices":["reject","queue","steer"],"key":"whenBusy","kind":"enum","option":"when-busy","repeated":false,"required":false}],"path":["codex","send"],"routeId":"cli:codex/send"},{"aliases":[],"description":"Probe the local Codex app-server daemon. Exit 0 only when it is usable.","exitCode":"result","options":[],"path":["codex","status"],"routeId":"cli:codex/status"},{"aliases":[],"description":"Bounded Codex wait observation; never interrupts execution.","exitCode":"result","options":[{"key":"expectedCwd","kind":"string","option":"expected-cwd","repeated":false,"required":false},{"description":"Reply budget: 1-4194304 bytes.","key":"maxOutputBytes","kind":"number","option":"max-output-bytes","repeated":false,"required":false},{"key":"messageId","kind":"string","option":"message-id","repeated":false,"required":false},{"key":"threadId","kind":"string","option":"thread-id","positional":0,"repeated":false,"required":true},{"description":"Observation timeout: 1-7200000 milliseconds.","key":"timeoutMs","kind":"number","option":"timeout-ms","repeated":false,"required":false},{"key":"turnId","kind":"string","option":"turn-id","positional":1,"repeated":false,"required":true}],"path":["codex","wait"],"routeId":"cli:codex/wait"},{"aliases":[],"description":"Bounded Codex watch observation; never interrupts execution.","exitCode":"result","options":[{"key":"expectedCwd","kind":"string","option":"expected-cwd","repeated":false,"required":false},{"description":"Maximum observed events: 1-500.","key":"maxEvents","kind":"number","option":"max-events","repeated":false,"required":false},{"key":"threadId","kind":"string","option":"thread-id","positional":0,"repeated":false,"required":true},{"description":"Observation timeout: 1-600000 milliseconds.","key":"timeoutMs","kind":"number","option":"timeout-ms","repeated":false,"required":false}],"path":["codex","watch"],"routeId":"cli:codex/watch"},{"aliases":[],"description":"Show which agents root and auth sources gbot can see.","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false}],"path":["doctor"],"routeId":"cli:doctor"},{"aliases":[],"description":"Add a bot to a group.","exitCode":"zero","options":[{"key":"bot","kind":"string","option":"bot","positional":1,"repeated":false,"required":true},{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"key":"group","kind":"string","option":"group","positional":0,"repeated":false,"required":true}],"path":["groups","add"],"routeId":"cli:groups/add"},{"aliases":[],"description":"Create a Grok Bot group.","exitCode":"zero","options":[{"key":"avatarColor","kind":"string","option":"avatar-color","repeated":false,"required":false},{"key":"avatarShape","kind":"string","option":"avatar-shape","repeated":false,"required":false},{"key":"description","kind":"string","option":"description","repeated":false,"required":false},{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"description":"Member bot id or name (repeatable)","key":"member","kind":"string","option":"member","repeated":true,"required":true},{"key":"name","kind":"string","option":"name","repeated":false,"required":true},{"key":"title","kind":"string","option":"title","repeated":false,"required":false}],"path":["groups","create"],"routeId":"cli:groups/create"},{"aliases":[],"description":"Delete a group by id or name.","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"description":"Group id or name","key":"ref","kind":"string","option":"ref","positional":0,"repeated":false,"required":true}],"path":["groups","delete"],"routeId":"cli:groups/delete"},{"aliases":[],"description":"Show one group by id or name.","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"description":"Group id or name","key":"ref","kind":"string","option":"ref","positional":0,"repeated":false,"required":true}],"path":["groups","get"],"routeId":"cli:groups/get"},{"aliases":[],"description":"List Grok Bot groups.","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false}],"path":["groups","list"],"routeId":"cli:groups/list"},{"aliases":[],"description":"Show members of a group (same payload as groups get).","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"description":"Group id or name","key":"ref","kind":"string","option":"ref","positional":0,"repeated":false,"required":true}],"path":["groups","members"],"routeId":"cli:groups/members"},{"aliases":[],"description":"Remove a bot from a group.","exitCode":"zero","options":[{"key":"bot","kind":"string","option":"bot","positional":1,"repeated":false,"required":true},{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"key":"group","kind":"string","option":"group","positional":0,"repeated":false,"required":true}],"path":["groups","remove"],"routeId":"cli:groups/remove"},{"aliases":[],"description":"Replace a group's member list.","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"key":"group","kind":"string","option":"group","positional":0,"repeated":false,"required":true},{"description":"Member bot id or name (repeatable)","key":"member","kind":"string","option":"member","repeated":true,"required":true}],"path":["groups","set"],"routeId":"cli:groups/set"},{"aliases":[],"description":"Update a group profile (members stay on set/add/remove).","exitCode":"zero","options":[{"key":"avatarColor","kind":"string","option":"avatar-color","repeated":false,"required":false},{"key":"avatarShape","kind":"string","option":"avatar-shape","repeated":false,"required":false},{"key":"description","kind":"string","option":"description","repeated":false,"required":false},{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"key":"hidden","kind":"string","option":"hidden","repeated":false,"required":false},{"key":"name","kind":"string","option":"name","repeated":false,"required":false},{"key":"notify","kind":"string","option":"notify","repeated":false,"required":false},{"description":"Group id or name","key":"ref","kind":"string","option":"ref","positional":0,"repeated":false,"required":true},{"key":"title","kind":"string","option":"title","repeated":false,"required":false}],"path":["groups","update"],"routeId":"cli:groups/update"},{"aliases":[],"description":"Read the opt-in local JSONL history without contacting the gateway.","exitCode":"zero","options":[{"description":"Directory containing history.jsonl","key":"historyDir","kind":"string","option":"history-dir","repeated":false,"required":false},{"description":"Maximum matching rows (1 or more)","key":"limit","kind":"number","option":"limit","repeated":false,"required":false},{"description":"Print the history file path","key":"path","kind":"boolean","option":"path","repeated":false,"required":false},{"description":"Bot or group id or name","key":"ref","kind":"string","option":"ref","positional":0,"repeated":false,"required":false},{"description":"Case-insensitive message text filter","key":"search","kind":"string","option":"search","repeated":false,"required":false}],"path":["history"],"routeId":"cli:history"},{"aliases":[],"description":"Send a message to a Grok Bot bot or group by name or id.","exitCode":"result","options":[{"key":"bindingId","kind":"string","option":"binding-id","repeated":false,"required":false},{"key":"codexThreadId","kind":"string","option":"codex-thread-id","repeated":false,"required":false},{"description":"Stable correlation id for multi-hop replies","key":"correlationId","kind":"string","option":"correlation-id","repeated":false,"required":false},{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Prepend the [gbot …] header","key":"envelope","kind":"boolean","option":"envelope","repeated":false,"required":false},{"key":"expectedCwd","kind":"string","option":"expected-cwd","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"description":"Directory containing history.jsonl","key":"historyDir","kind":"string","option":"history-dir","repeated":false,"required":false},{"description":"Hop count; refused at GROK_BOT_MAX_HOPS","key":"hop","kind":"number","option":"hop","repeated":false,"required":false},{"key":"message","kind":"string","option":"message","positional":1,"repeated":true,"required":true},{"description":"Skip local history for this command","key":"noHistory","kind":"boolean","option":"no-history","repeated":false,"required":false},{"choices":["auto","manual"],"key":"replyMode","kind":"enum","option":"reply-mode","repeated":false,"required":false},{"description":"Prior message id this send replies to","key":"replyTo","kind":"string","option":"reply-to","repeated":false,"required":false},{"key":"requestId","kind":"string","option":"request-id","repeated":false,"required":false},{"key":"target","kind":"string","option":"target","positional":0,"repeated":false,"required":true}],"path":["send"],"routeId":"cli:send"},{"aliases":[],"description":"Add a SKILL.md to the shared skill library. Every bot sees it.","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"description":"SKILL.md file, or a directory holding one","key":"path","kind":"string","option":"path","positional":0,"repeated":false,"required":true}],"path":["skills","add"],"routeId":"cli:skills/add"},{"aliases":[],"description":"List the skill library every bot in this Grok Bot shares.","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false}],"path":["skills","list"],"routeId":"cli:skills/list"},{"aliases":[],"description":"Remove a library skill by id or name. Every bot loses it.","exitCode":"zero","options":[{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"description":"Skill id or name","key":"skill","kind":"string","option":"skill","positional":0,"repeated":false,"required":true}],"path":["skills","remove"],"routeId":"cli:skills/remove"},{"aliases":[],"description":"Read the most recent messages in a Grok Bot bot or group thread.","exitCode":"zero","options":[{"description":"Return entries after this opaque entry id","key":"after","kind":"string","option":"after","repeated":false,"required":false},{"description":"Agents directory for --files mode","key":"dir","kind":"string","option":"dir","repeated":false,"required":false},{"description":"Force the on-disk agents store","key":"files","kind":"boolean","option":"files","repeated":false,"required":false},{"description":"Show full entry text in human output","key":"full","kind":"boolean","option":"full","repeated":false,"required":false},{"description":"Force the live gateway","key":"gateway","kind":"boolean","option":"gateway","repeated":false,"required":false},{"description":"Directory containing history.jsonl","key":"historyDir","kind":"string","option":"history-dir","repeated":false,"required":false},{"description":"How many trailing entries to return (1-200)","key":"limit","kind":"number","option":"limit","repeated":false,"required":false},{"description":"Skip local history for this command","key":"noHistory","kind":"boolean","option":"no-history","repeated":false,"required":false},{"description":"Read one rooted thread by message id","key":"root","kind":"string","option":"root","repeated":false,"required":false},{"key":"target","kind":"string","option":"target","positional":0,"repeated":false,"required":true}],"path":["thread"],"routeId":"cli:thread"}],"mode":"generated","routes":[{"contract":"contract:src/cli/approvals/list.tsx#inputJsonSchema","description":"List current Grok approval cards (latest 200 entries).","id":"cli:approvals/list","inputSchema":{"additionalProperties":false,"properties":{"target":{"type":"string"}},"required":["target"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/approvals/list.tsx"},{"contract":"contract:src/cli/approvals/respond.tsx#inputJsonSchema","description":"Explicit user decision: accept a Grok request once or decline; never grant persistent permissions.","id":"cli:approvals/respond","inputSchema":{"additionalProperties":false,"properties":{"decision":{"enum":["accept","decline"],"type":"string"},"entryId":{"type":"string"},"requestId":{"type":"string"},"target":{"type":"string"}},"required":["target","entryId","requestId","decision"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/approvals/respond.tsx"},{"contract":"contract:src/cli/bots/create.tsx#inputJsonSchema","description":"Create a Grok Bot bot.","id":"cli:bots/create","inputSchema":{"additionalProperties":false,"properties":{"avatarColor":{"type":"string"},"avatarShape":{"type":"string"},"description":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"name":{"type":"string"},"title":{"type":"string"}},"required":["name"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/bots/create.tsx"},{"contract":"contract:src/cli/bots/delete.tsx#inputJsonSchema","description":"Delete a bot or group by id or name.","id":"cli:bots/delete","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"ref":{"description":"Bot or group id or name","type":"string"}},"required":["ref"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/bots/delete.tsx"},{"contract":"contract:src/cli/bots/get.tsx#inputJsonSchema","description":"Show one bot or group by id or name.","id":"cli:bots/get","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"ref":{"description":"Bot or group id or name","type":"string"}},"required":["ref"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/bots/get.tsx"},{"contract":"contract:src/cli/bots/list.tsx#inputJsonSchema","description":"List Grok Bot bots (not groups).","id":"cli:bots/list","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/bots/list.tsx"},{"contract":"contract:src/cli/bots/update.tsx#inputJsonSchema","description":"Update a bot or group profile.","id":"cli:bots/update","inputSchema":{"additionalProperties":false,"properties":{"avatarColor":{"type":"string"},"avatarShape":{"type":"string"},"description":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"hidden":{"type":"string"},"name":{"type":"string"},"notify":{"type":"string"},"ref":{"description":"Bot or group id or name","type":"string"},"title":{"type":"string"}},"required":["ref"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/bots/update.tsx"},{"contract":"contract:src/cli/chatgpt-desktop/hosts.tsx#inputJsonSchema","description":"Discover ChatGPT Desktop hosts and modelProviders at runtime (never hardcode). Always includes local plus every remote-thread-summaries-v3:; Providers come from distinct thread/list and remote-summary values.","id":"cli:chatgpt-desktop/hosts","inputSchema":{"additionalProperties":false,"properties":{"port":{"description":"Local CDP port","type":"number"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/chatgpt-desktop/hosts.tsx"},{"contract":"contract:src/cli/chatgpt-desktop/read.tsx#inputJsonSchema","description":"Read a ChatGPT Desktop thread via app-server (turns/list); CDP DOM is fallback only.","id":"cli:chatgpt-desktop/read","inputSchema":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from the prior read page","type":"string"},"full":{"default":false,"type":"boolean"},"limit":{"description":"Recent default 100; full default 2000","type":"number"},"openTimeoutMs":{"default":90000,"type":"number"},"port":{"type":"number"},"threadId":{"type":"string"}},"required":["threadId"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/chatgpt-desktop/read.tsx"},{"contract":"contract:src/cli/chatgpt-desktop/search.tsx#inputJsonSchema","description":"Search ChatGPT Desktop threads. --host and --model-provider accept any string (see gbot chatgpt-desktop hosts); not enums.","id":"cli:chatgpt-desktop/search","inputSchema":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from a prior search page","type":"string"},"groupBy":{"description":"Group results by host (grouping mode, not a host id)","enum":["host"],"type":"string"},"host":{"description":"Any string: all (default), local, or a hostId/friendly name from `gbot chatgpt-desktop hosts`. Not an enum.","type":"string"},"limit":{"default":50,"description":"Max matches (1-200)","type":"number"},"modelProvider":{"description":"Any modelProvider id (app-server modelProviders pass-through). Omit = all. See `gbot chatgpt-desktop hosts`. Not an enum.","type":"string"},"port":{"description":"Local CDP port","type":"number"},"project":{"description":"Project label or projectId filter.","type":"string"},"query":{"type":"string"}},"required":["query"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/chatgpt-desktop/search.tsx"},{"contract":"contract:src/cli/chatgpt-desktop/send.tsx#inputJsonSchema","description":"Send to ChatGPT Desktop via CDP composer. Omit --thread-id for a new chat; --project prefers Start new chat in .","id":"cli:chatgpt-desktop/send","inputSchema":{"additionalProperties":false,"properties":{"openTimeoutMs":{"type":"number"},"port":{"type":"number"},"project":{"type":"string"},"text":{"items":{"type":"string"},"type":"array"},"threadId":{"type":"string"},"unarchive":{"description":"Explicitly unarchive an existing archived thread before sending.","type":"boolean"}},"required":["text"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/chatgpt-desktop/send.tsx"},{"contract":"contract:src/cli/chatgpt-desktop/status.tsx#inputJsonSchema","description":"Probe local ChatGPT Desktop CDP on 127.0.0.1 and report app-server availability for list/deep-read.","id":"cli:chatgpt-desktop/status","inputSchema":{"additionalProperties":false,"properties":{"port":{"description":"Local CDP port (default 9222)","type":"number"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/chatgpt-desktop/status.tsx"},{"contract":"contract:src/cli/chatgpt-desktop/threads.tsx#inputJsonSchema","description":"List local + remote-control ChatGPT Desktop threads. --host and --model-provider accept any string (see gbot chatgpt-desktop hosts); not enums.","id":"cli:chatgpt-desktop/threads","inputSchema":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from a previous page","type":"string"},"groupBy":{"description":"Group results by host (grouping mode, not a host id)","enum":["host"],"type":"string"},"host":{"description":"Any string: all (default), local, or a hostId/friendly name from `gbot chatgpt-desktop hosts`. Not an enum.","type":"string"},"limit":{"default":50,"description":"Max threads (1-200)","type":"number"},"modelProvider":{"description":"Any modelProvider id (app-server modelProviders pass-through). Omit = all. See `gbot chatgpt-desktop hosts`. Not an enum.","type":"string"},"port":{"description":"Local CDP port","type":"number"},"project":{"description":"Project label or projectId filter.","type":"string"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/chatgpt-desktop/threads.tsx"},{"contract":"contract:src/cli/claude/send.tsx#inputJsonSchema","description":"Send to an explicitly enabled live Claude Code channel and wait for its reply.","id":"cli:claude/send","inputSchema":{"additionalProperties":false,"properties":{"message":{"type":"string"},"name":{"type":"string"},"timeoutMs":{"type":"number"}},"required":["name","message"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/claude/send.tsx"},{"contract":"contract:src/cli/codex/bridge/respond.tsx#inputJsonSchema","description":"Managed Grok/Codex bridge respond.","id":"cli:codex/bridge/respond","inputSchema":{"additionalProperties":false,"properties":{"answersJson":{"description":"JSON object mapping each advertised question ID to {\"answers\":[\"answer\"]}; exact IDs required.","type":"string"},"bindingId":{"type":"string"},"decision":{"enum":["accept","decline","cancel"],"type":"string"},"exchangeId":{"type":"string"},"generation":{"type":"string"},"interactionId":{"type":"string"},"threadId":{"type":"string"},"turnId":{"type":"string"}},"required":["interactionId","generation","threadId","turnId"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/bridge/respond.tsx"},{"contract":"contract:src/cli/codex/bridge/run.tsx#inputJsonSchema","description":"Run a bounded foreground relay (up to 23 hours). Use the packaged gbot-relay.mjs script for unlimited service lifetime.","id":"cli:codex/bridge/run","inputSchema":{"additionalProperties":false,"properties":{"lifetimeMs":{"description":"Foreground lifetime in milliseconds (100..82800000; default 23h).","type":"number"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/bridge/run.tsx"},{"contract":"contract:src/cli/codex/bridge/start.tsx#inputJsonSchema","description":"Managed Grok/Codex bridge start.","id":"cli:codex/bridge/start","inputSchema":{"additionalProperties":false,"properties":{"busyPolicy":{"enum":["steer","reject"],"type":"string"},"codexThreadId":{"type":"string"},"expectedCwd":{"type":"string"},"grokTarget":{"type":"string"},"requestId":{"type":"string"}},"required":["grokTarget"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/bridge/start.tsx"},{"contract":"contract:src/cli/codex/bridge/status.tsx#inputJsonSchema","description":"Managed Grok/Codex bridge status.","id":"cli:codex/bridge/status","inputSchema":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"limit":{"type":"number"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/bridge/status.tsx"},{"contract":"contract:src/cli/codex/bridge/stop.tsx#inputJsonSchema","description":"Managed Grok/Codex bridge stop.","id":"cli:codex/bridge/stop","inputSchema":{"additionalProperties":false,"properties":{"all":{"type":"boolean"},"bindingId":{"type":"string"},"worker":{"type":"boolean"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/bridge/stop.tsx"},{"contract":"contract:src/cli/codex/desktop-shim.tsx#inputJsonSchema","description":"Install, remove, or check the ChatGPT Desktop shim: a CODEX_CLI_PATH wrapper that bridges Desktop stdio onto the managed Codex daemon (stock app-server proxy hangs). macOS persists via LaunchAgent; always fails open to real Codex.","id":"cli:codex/desktop-shim","inputSchema":{"additionalProperties":false,"properties":{"action":{"description":"install | uninstall | status","enum":["install","uninstall","status"],"type":"string"}},"required":["action"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/desktop-shim.tsx"},{"contract":"contract:src/cli/codex/list-threads.tsx#inputJsonSchema","description":"List Codex daemon-managed threads, newest activity first (all modelProviders; pages via --cursor / nextCursor). Find old threads with --query (name/title/id prefix, all pages), --active-within 7d, --since, --sort created.","id":"cli:codex/list-threads","inputSchema":{"additionalProperties":false,"properties":{"activeWithin":{"description":"Only threads updated within 90s, 30m, 12h, 7d, 2w","type":"string"},"archived":{"description":"Archived threads only","type":"boolean"},"cursor":{"description":"Opaque pagination cursor from a previous nextCursor (may start with -)","type":"string"},"cwd":{"description":"Exact session cwd","type":"string"},"limit":{"default":20,"description":"Max threads to list (1-200)","type":"number"},"modelProvider":{"description":"Any modelProvider id; omit for all","type":"string"},"order":{"default":"desc","enum":["desc","asc"],"type":"string"},"query":{"description":"Case-insensitive substring of name/title, preview or id (id prefix works); scans every page","type":"string"},"since":{"description":"Only threads updated at/after this ISO date/time or epoch seconds","type":"string"},"sort":{"default":"updated","description":"updated (last activity, default) | created | recency","enum":["updated","created","recency"],"type":"string"},"sourceKind":{"description":"Any app-server source kind; omit for interactive sources","type":"string"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/list-threads.tsx"},{"contract":"contract:src/cli/codex/new.tsx#inputJsonSchema","description":"Start a new Codex thread in --cwd, optionally with --model, --effort and a message.","id":"cli:codex/new","inputSchema":{"additionalProperties":false,"properties":{"cwd":{"type":"string"},"effort":{"enum":["none","minimal","low","medium","high","xhigh"],"type":"string"},"expectedCwd":{"type":"string"},"message":{"items":{"type":"string"},"type":"array"},"model":{"type":"string"}},"required":["cwd"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/new.tsx"},{"contract":"contract:src/cli/codex/queue.tsx#inputJsonSchema","description":"List the experimental Codex thread queue (GROK_BOT_CODEX_EXPERIMENTAL=1).","id":"cli:codex/queue","inputSchema":{"additionalProperties":false,"properties":{"threadId":{"type":"string"}},"required":["threadId"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/queue.tsx"},{"contract":"contract:src/cli/codex/send.tsx#inputJsonSchema","description":"Send to Codex; acceptance is distinct from completion. Options precede threadId.","id":"cli:codex/send","inputSchema":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"correlationId":{"type":"string"},"effort":{"enum":["none","minimal","low","medium","high","xhigh"],"type":"string"},"envelope":{"type":"boolean"},"expectedCwd":{"type":"string"},"expectedTurnId":{"description":"Required active-turn guard for steer; stale guards reject.","type":"string"},"hop":{"type":"number"},"maxOutputBytes":{"description":"Reply budget: 1-4194304 bytes.","type":"number"},"message":{"items":{"type":"string"},"type":"array"},"model":{"type":"string"},"replyTo":{"type":"string"},"replyToGrok":{"type":"string"},"requestId":{"type":"string"},"threadId":{"type":"string"},"timeoutMs":{"description":"Observation timeout: 1-7200000 milliseconds.","type":"number"},"wait":{"type":"boolean"},"whenBusy":{"enum":["reject","queue","steer"],"type":"string"}},"required":["threadId","message"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/send.tsx"},{"contract":"contract:src/cli/codex/status.tsx#inputJsonSchema","description":"Probe the local Codex app-server daemon. Exit 0 only when it is usable.","id":"cli:codex/status","inputSchema":{"additionalProperties":false,"properties":{},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/status.tsx"},{"contract":"contract:src/cli/codex/wait.tsx#inputJsonSchema","description":"Bounded Codex wait observation; never interrupts execution.","id":"cli:codex/wait","inputSchema":{"additionalProperties":false,"properties":{"expectedCwd":{"type":"string"},"maxOutputBytes":{"description":"Reply budget: 1-4194304 bytes.","type":"number"},"messageId":{"type":"string"},"threadId":{"type":"string"},"timeoutMs":{"description":"Observation timeout: 1-7200000 milliseconds.","type":"number"},"turnId":{"type":"string"}},"required":["threadId","turnId"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/wait.tsx"},{"contract":"contract:src/cli/codex/watch.tsx#inputJsonSchema","description":"Bounded Codex watch observation; never interrupts execution.","id":"cli:codex/watch","inputSchema":{"additionalProperties":false,"properties":{"expectedCwd":{"type":"string"},"maxEvents":{"description":"Maximum observed events: 1-500.","type":"number"},"threadId":{"type":"string"},"timeoutMs":{"description":"Observation timeout: 1-600000 milliseconds.","type":"number"}},"required":["threadId"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/codex/watch.tsx"},{"contract":"contract:src/cli/doctor.tsx#inputJsonSchema","description":"Show which agents root and auth sources gbot can see.","id":"cli:doctor","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/doctor.tsx"},{"contract":"contract:src/cli/groups/add.tsx#inputJsonSchema","description":"Add a bot to a group.","id":"cli:groups/add","inputSchema":{"additionalProperties":false,"properties":{"bot":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"group":{"type":"string"}},"required":["group","bot"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/groups/add.tsx"},{"contract":"contract:src/cli/groups/create.tsx#inputJsonSchema","description":"Create a Grok Bot group.","id":"cli:groups/create","inputSchema":{"additionalProperties":false,"properties":{"avatarColor":{"type":"string"},"avatarShape":{"type":"string"},"description":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"member":{"description":"Member bot id or name (repeatable)","items":{"type":"string"},"type":"array"},"name":{"type":"string"},"title":{"type":"string"}},"required":["name","member"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/groups/create.tsx"},{"contract":"contract:src/cli/groups/delete.tsx#inputJsonSchema","description":"Delete a group by id or name.","id":"cli:groups/delete","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"ref":{"description":"Group id or name","type":"string"}},"required":["ref"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/groups/delete.tsx"},{"contract":"contract:src/cli/groups/get.tsx#inputJsonSchema","description":"Show one group by id or name.","id":"cli:groups/get","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"ref":{"description":"Group id or name","type":"string"}},"required":["ref"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/groups/get.tsx"},{"contract":"contract:src/cli/groups/list.tsx#inputJsonSchema","description":"List Grok Bot groups.","id":"cli:groups/list","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/groups/list.tsx"},{"contract":"contract:src/cli/groups/members.tsx#inputJsonSchema","description":"Show members of a group (same payload as groups get).","id":"cli:groups/members","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"ref":{"description":"Group id or name","type":"string"}},"required":["ref"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/groups/members.tsx"},{"contract":"contract:src/cli/groups/remove.tsx#inputJsonSchema","description":"Remove a bot from a group.","id":"cli:groups/remove","inputSchema":{"additionalProperties":false,"properties":{"bot":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"group":{"type":"string"}},"required":["group","bot"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/groups/remove.tsx"},{"contract":"contract:src/cli/groups/set.tsx#inputJsonSchema","description":"Replace a group's member list.","id":"cli:groups/set","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"group":{"type":"string"},"member":{"description":"Member bot id or name (repeatable)","items":{"type":"string"},"type":"array"}},"required":["group","member"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/groups/set.tsx"},{"contract":"contract:src/cli/groups/update.tsx#inputJsonSchema","description":"Update a group profile (members stay on set/add/remove).","id":"cli:groups/update","inputSchema":{"additionalProperties":false,"properties":{"avatarColor":{"type":"string"},"avatarShape":{"type":"string"},"description":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"hidden":{"type":"string"},"name":{"type":"string"},"notify":{"type":"string"},"ref":{"description":"Group id or name","type":"string"},"title":{"type":"string"}},"required":["ref"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/groups/update.tsx"},{"contract":"contract:src/cli/history.tsx#inputJsonSchema","description":"Read the opt-in local JSONL history without contacting the gateway.","id":"cli:history","inputSchema":{"additionalProperties":false,"properties":{"historyDir":{"description":"Directory containing history.jsonl","type":"string"},"limit":{"default":40,"description":"Maximum matching rows (1 or more)","type":"number"},"path":{"description":"Print the history file path","type":"boolean"},"ref":{"description":"Bot or group id or name","type":"string"},"search":{"description":"Case-insensitive message text filter","type":"string"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/history.tsx"},{"contract":"contract:src/cli/send.tsx#inputJsonSchema","description":"Send a message to a Grok Bot bot or group by name or id.","id":"cli:send","inputSchema":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"codexThreadId":{"type":"string"},"correlationId":{"description":"Stable correlation id for multi-hop replies","type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"envelope":{"description":"Prepend the [gbot …] header","type":"boolean"},"expectedCwd":{"type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"historyDir":{"description":"Directory containing history.jsonl","type":"string"},"hop":{"description":"Hop count; refused at GROK_BOT_MAX_HOPS","type":"number"},"message":{"items":{"type":"string"},"type":"array"},"noHistory":{"description":"Skip local history for this command","type":"boolean"},"replyMode":{"enum":["auto","manual"],"type":"string"},"replyTo":{"description":"Prior message id this send replies to","type":"string"},"requestId":{"type":"string"},"target":{"type":"string"}},"required":["target","message"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/send.tsx"},{"contract":"contract:src/cli/skills/add.tsx#inputJsonSchema","description":"Add a SKILL.md to the shared skill library. Every bot sees it.","id":"cli:skills/add","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"path":{"description":"SKILL.md file, or a directory holding one","type":"string"}},"required":["path"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/skills/add.tsx"},{"contract":"contract:src/cli/skills/list.tsx#inputJsonSchema","description":"List the skill library every bot in this Grok Bot shares.","id":"cli:skills/list","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"}},"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/skills/list.tsx"},{"contract":"contract:src/cli/skills/remove.tsx#inputJsonSchema","description":"Remove a library skill by id or name. Every bot loses it.","id":"cli:skills/remove","inputSchema":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"skill":{"description":"Skill id or name","type":"string"}},"required":["skill"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/skills/remove.tsx"},{"contract":"contract:src/cli/thread.tsx#inputJsonSchema","description":"Read the most recent messages in a Grok Bot bot or group thread.","id":"cli:thread","inputSchema":{"additionalProperties":false,"properties":{"after":{"description":"Return entries after this opaque entry id","type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"full":{"description":"Show full entry text in human output","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"historyDir":{"description":"Directory containing history.jsonl","type":"string"},"limit":{"default":40,"description":"How many trailing entries to return (1-200)","type":"number"},"noHistory":{"description":"Skip local history for this command","type":"boolean"},"root":{"description":"Read one rooted thread by message id","type":"string"},"target":{"type":"string"}},"required":["target"],"type":"object"},"kind":"cli","provenance":{"kind":"conventional"},"source":"src/cli/thread.tsx"}]},"contracts":[{"id":"contract:src/cli/approvals/list.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"target":{"type":"string"}},"required":["target"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/approvals/list.tsx"},"routes":["cli:approvals/list"]},{"id":"contract:src/cli/approvals/respond.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"decision":{"enum":["accept","decline"],"type":"string"},"entryId":{"type":"string"},"requestId":{"type":"string"},"target":{"type":"string"}},"required":["target","entryId","requestId","decision"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/approvals/respond.tsx"},"routes":["cli:approvals/respond"]},{"id":"contract:src/cli/bots/create.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"avatarColor":{"type":"string"},"avatarShape":{"type":"string"},"description":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"name":{"type":"string"},"title":{"type":"string"}},"required":["name"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/bots/create.tsx"},"routes":["cli:bots/create"]},{"id":"contract:src/cli/bots/delete.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"ref":{"description":"Bot or group id or name","type":"string"}},"required":["ref"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/bots/delete.tsx"},"routes":["cli:bots/delete"]},{"id":"contract:src/cli/bots/get.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"ref":{"description":"Bot or group id or name","type":"string"}},"required":["ref"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/bots/get.tsx"},"routes":["cli:bots/get"]},{"id":"contract:src/cli/bots/list.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/bots/list.tsx"},"routes":["cli:bots/list"]},{"id":"contract:src/cli/bots/update.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"avatarColor":{"type":"string"},"avatarShape":{"type":"string"},"description":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"hidden":{"type":"string"},"name":{"type":"string"},"notify":{"type":"string"},"ref":{"description":"Bot or group id or name","type":"string"},"title":{"type":"string"}},"required":["ref"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/bots/update.tsx"},"routes":["cli:bots/update"]},{"id":"contract:src/cli/chatgpt-desktop/hosts.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"port":{"description":"Local CDP port","type":"number"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/chatgpt-desktop/hosts.tsx"},"routes":["cli:chatgpt-desktop/hosts"]},{"id":"contract:src/cli/chatgpt-desktop/read.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from the prior read page","type":"string"},"full":{"default":false,"type":"boolean"},"limit":{"description":"Recent default 100; full default 2000","type":"number"},"openTimeoutMs":{"default":90000,"type":"number"},"port":{"type":"number"},"threadId":{"type":"string"}},"required":["threadId"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/chatgpt-desktop/read.tsx"},"routes":["cli:chatgpt-desktop/read"]},{"id":"contract:src/cli/chatgpt-desktop/search.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from a prior search page","type":"string"},"groupBy":{"description":"Group results by host (grouping mode, not a host id)","enum":["host"],"type":"string"},"host":{"description":"Any string: all (default), local, or a hostId/friendly name from `gbot chatgpt-desktop hosts`. Not an enum.","type":"string"},"limit":{"default":50,"description":"Max matches (1-200)","type":"number"},"modelProvider":{"description":"Any modelProvider id (app-server modelProviders pass-through). Omit = all. See `gbot chatgpt-desktop hosts`. Not an enum.","type":"string"},"port":{"description":"Local CDP port","type":"number"},"project":{"description":"Project label or projectId filter.","type":"string"},"query":{"type":"string"}},"required":["query"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/chatgpt-desktop/search.tsx"},"routes":["cli:chatgpt-desktop/search"]},{"id":"contract:src/cli/chatgpt-desktop/send.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"openTimeoutMs":{"type":"number"},"port":{"type":"number"},"project":{"type":"string"},"text":{"items":{"type":"string"},"type":"array"},"threadId":{"type":"string"},"unarchive":{"description":"Explicitly unarchive an existing archived thread before sending.","type":"boolean"}},"required":["text"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/chatgpt-desktop/send.tsx"},"routes":["cli:chatgpt-desktop/send"]},{"id":"contract:src/cli/chatgpt-desktop/status.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"port":{"description":"Local CDP port (default 9222)","type":"number"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/chatgpt-desktop/status.tsx"},"routes":["cli:chatgpt-desktop/status"]},{"id":"contract:src/cli/chatgpt-desktop/threads.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from a previous page","type":"string"},"groupBy":{"description":"Group results by host (grouping mode, not a host id)","enum":["host"],"type":"string"},"host":{"description":"Any string: all (default), local, or a hostId/friendly name from `gbot chatgpt-desktop hosts`. Not an enum.","type":"string"},"limit":{"default":50,"description":"Max threads (1-200)","type":"number"},"modelProvider":{"description":"Any modelProvider id (app-server modelProviders pass-through). Omit = all. See `gbot chatgpt-desktop hosts`. Not an enum.","type":"string"},"port":{"description":"Local CDP port","type":"number"},"project":{"description":"Project label or projectId filter.","type":"string"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/chatgpt-desktop/threads.tsx"},"routes":["cli:chatgpt-desktop/threads"]},{"id":"contract:src/cli/claude/send.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"message":{"type":"string"},"name":{"type":"string"},"timeoutMs":{"type":"number"}},"required":["name","message"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/claude/send.tsx"},"routes":["cli:claude/send"]},{"id":"contract:src/cli/codex/bridge/respond.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"answersJson":{"description":"JSON object mapping each advertised question ID to {\"answers\":[\"answer\"]}; exact IDs required.","type":"string"},"bindingId":{"type":"string"},"decision":{"enum":["accept","decline","cancel"],"type":"string"},"exchangeId":{"type":"string"},"generation":{"type":"string"},"interactionId":{"type":"string"},"threadId":{"type":"string"},"turnId":{"type":"string"}},"required":["interactionId","generation","threadId","turnId"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/bridge/respond.tsx"},"routes":["cli:codex/bridge/respond"]},{"id":"contract:src/cli/codex/bridge/run.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"lifetimeMs":{"description":"Foreground lifetime in milliseconds (100..82800000; default 23h).","type":"number"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/bridge/run.tsx"},"routes":["cli:codex/bridge/run"]},{"id":"contract:src/cli/codex/bridge/start.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"busyPolicy":{"enum":["steer","reject"],"type":"string"},"codexThreadId":{"type":"string"},"expectedCwd":{"type":"string"},"grokTarget":{"type":"string"},"requestId":{"type":"string"}},"required":["grokTarget"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/bridge/start.tsx"},"routes":["cli:codex/bridge/start"]},{"id":"contract:src/cli/codex/bridge/status.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"limit":{"type":"number"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/bridge/status.tsx"},"routes":["cli:codex/bridge/status"]},{"id":"contract:src/cli/codex/bridge/stop.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"all":{"type":"boolean"},"bindingId":{"type":"string"},"worker":{"type":"boolean"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/bridge/stop.tsx"},"routes":["cli:codex/bridge/stop"]},{"id":"contract:src/cli/codex/desktop-shim.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"action":{"description":"install | uninstall | status","enum":["install","uninstall","status"],"type":"string"}},"required":["action"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/desktop-shim.tsx"},"routes":["cli:codex/desktop-shim"]},{"id":"contract:src/cli/codex/list-threads.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"activeWithin":{"description":"Only threads updated within 90s, 30m, 12h, 7d, 2w","type":"string"},"archived":{"description":"Archived threads only","type":"boolean"},"cursor":{"description":"Opaque pagination cursor from a previous nextCursor (may start with -)","type":"string"},"cwd":{"description":"Exact session cwd","type":"string"},"limit":{"default":20,"description":"Max threads to list (1-200)","type":"number"},"modelProvider":{"description":"Any modelProvider id; omit for all","type":"string"},"order":{"default":"desc","enum":["desc","asc"],"type":"string"},"query":{"description":"Case-insensitive substring of name/title, preview or id (id prefix works); scans every page","type":"string"},"since":{"description":"Only threads updated at/after this ISO date/time or epoch seconds","type":"string"},"sort":{"default":"updated","description":"updated (last activity, default) | created | recency","enum":["updated","created","recency"],"type":"string"},"sourceKind":{"description":"Any app-server source kind; omit for interactive sources","type":"string"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/list-threads.tsx"},"routes":["cli:codex/list-threads"]},{"id":"contract:src/cli/codex/new.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"cwd":{"type":"string"},"effort":{"enum":["none","minimal","low","medium","high","xhigh"],"type":"string"},"expectedCwd":{"type":"string"},"message":{"items":{"type":"string"},"type":"array"},"model":{"type":"string"}},"required":["cwd"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/new.tsx"},"routes":["cli:codex/new"]},{"id":"contract:src/cli/codex/queue.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"threadId":{"type":"string"}},"required":["threadId"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/queue.tsx"},"routes":["cli:codex/queue"]},{"id":"contract:src/cli/codex/send.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"correlationId":{"type":"string"},"effort":{"enum":["none","minimal","low","medium","high","xhigh"],"type":"string"},"envelope":{"type":"boolean"},"expectedCwd":{"type":"string"},"expectedTurnId":{"description":"Required active-turn guard for steer; stale guards reject.","type":"string"},"hop":{"type":"number"},"maxOutputBytes":{"description":"Reply budget: 1-4194304 bytes.","type":"number"},"message":{"items":{"type":"string"},"type":"array"},"model":{"type":"string"},"replyTo":{"type":"string"},"replyToGrok":{"type":"string"},"requestId":{"type":"string"},"threadId":{"type":"string"},"timeoutMs":{"description":"Observation timeout: 1-7200000 milliseconds.","type":"number"},"wait":{"type":"boolean"},"whenBusy":{"enum":["reject","queue","steer"],"type":"string"}},"required":["threadId","message"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/send.tsx"},"routes":["cli:codex/send"]},{"id":"contract:src/cli/codex/status.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/status.tsx"},"routes":["cli:codex/status"]},{"id":"contract:src/cli/codex/wait.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"expectedCwd":{"type":"string"},"maxOutputBytes":{"description":"Reply budget: 1-4194304 bytes.","type":"number"},"messageId":{"type":"string"},"threadId":{"type":"string"},"timeoutMs":{"description":"Observation timeout: 1-7200000 milliseconds.","type":"number"},"turnId":{"type":"string"}},"required":["threadId","turnId"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/wait.tsx"},"routes":["cli:codex/wait"]},{"id":"contract:src/cli/codex/watch.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"expectedCwd":{"type":"string"},"maxEvents":{"description":"Maximum observed events: 1-500.","type":"number"},"threadId":{"type":"string"},"timeoutMs":{"description":"Observation timeout: 1-600000 milliseconds.","type":"number"}},"required":["threadId"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/codex/watch.tsx"},"routes":["cli:codex/watch"]},{"id":"contract:src/cli/doctor.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/doctor.tsx"},"routes":["cli:doctor"]},{"id":"contract:src/cli/groups/add.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"bot":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"group":{"type":"string"}},"required":["group","bot"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/groups/add.tsx"},"routes":["cli:groups/add"]},{"id":"contract:src/cli/groups/create.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"avatarColor":{"type":"string"},"avatarShape":{"type":"string"},"description":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"member":{"description":"Member bot id or name (repeatable)","items":{"type":"string"},"type":"array"},"name":{"type":"string"},"title":{"type":"string"}},"required":["name","member"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/groups/create.tsx"},"routes":["cli:groups/create"]},{"id":"contract:src/cli/groups/delete.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"ref":{"description":"Group id or name","type":"string"}},"required":["ref"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/groups/delete.tsx"},"routes":["cli:groups/delete"]},{"id":"contract:src/cli/groups/get.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"ref":{"description":"Group id or name","type":"string"}},"required":["ref"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/groups/get.tsx"},"routes":["cli:groups/get"]},{"id":"contract:src/cli/groups/list.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/groups/list.tsx"},"routes":["cli:groups/list"]},{"id":"contract:src/cli/groups/members.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"ref":{"description":"Group id or name","type":"string"}},"required":["ref"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/groups/members.tsx"},"routes":["cli:groups/members"]},{"id":"contract:src/cli/groups/remove.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"bot":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"group":{"type":"string"}},"required":["group","bot"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/groups/remove.tsx"},"routes":["cli:groups/remove"]},{"id":"contract:src/cli/groups/set.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"group":{"type":"string"},"member":{"description":"Member bot id or name (repeatable)","items":{"type":"string"},"type":"array"}},"required":["group","member"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/groups/set.tsx"},"routes":["cli:groups/set"]},{"id":"contract:src/cli/groups/update.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"avatarColor":{"type":"string"},"avatarShape":{"type":"string"},"description":{"type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"hidden":{"type":"string"},"name":{"type":"string"},"notify":{"type":"string"},"ref":{"description":"Group id or name","type":"string"},"title":{"type":"string"}},"required":["ref"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/groups/update.tsx"},"routes":["cli:groups/update"]},{"id":"contract:src/cli/history.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"historyDir":{"description":"Directory containing history.jsonl","type":"string"},"limit":{"default":40,"description":"Maximum matching rows (1 or more)","type":"number"},"path":{"description":"Print the history file path","type":"boolean"},"ref":{"description":"Bot or group id or name","type":"string"},"search":{"description":"Case-insensitive message text filter","type":"string"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/history.tsx"},"routes":["cli:history"]},{"id":"contract:src/cli/send.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"codexThreadId":{"type":"string"},"correlationId":{"description":"Stable correlation id for multi-hop replies","type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"envelope":{"description":"Prepend the [gbot …] header","type":"boolean"},"expectedCwd":{"type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"historyDir":{"description":"Directory containing history.jsonl","type":"string"},"hop":{"description":"Hop count; refused at GROK_BOT_MAX_HOPS","type":"number"},"message":{"items":{"type":"string"},"type":"array"},"noHistory":{"description":"Skip local history for this command","type":"boolean"},"replyMode":{"enum":["auto","manual"],"type":"string"},"replyTo":{"description":"Prior message id this send replies to","type":"string"},"requestId":{"type":"string"},"target":{"type":"string"}},"required":["target","message"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/send.tsx"},"routes":["cli:send"]},{"id":"contract:src/cli/skills/add.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"path":{"description":"SKILL.md file, or a directory holding one","type":"string"}},"required":["path"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/skills/add.tsx"},"routes":["cli:skills/add"]},{"id":"contract:src/cli/skills/list.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/skills/list.tsx"},"routes":["cli:skills/list"]},{"id":"contract:src/cli/skills/remove.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"skill":{"description":"Skill id or name","type":"string"}},"required":["skill"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/skills/remove.tsx"},"routes":["cli:skills/remove"]},{"id":"contract:src/cli/thread.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"after":{"description":"Return entries after this opaque entry id","type":"string"},"dir":{"description":"Agents directory for --files mode","type":"string"},"files":{"description":"Force the on-disk agents store","type":"boolean"},"full":{"description":"Show full entry text in human output","type":"boolean"},"gateway":{"description":"Force the live gateway","type":"boolean"},"historyDir":{"description":"Directory containing history.jsonl","type":"string"},"limit":{"default":40,"description":"How many trailing entries to return (1-200)","type":"number"},"noHistory":{"description":"Skip local history for this command","type":"boolean"},"root":{"description":"Read one rooted thread by message id","type":"string"},"target":{"type":"string"}},"required":["target"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/cli/thread.tsx"},"routes":["cli:thread"]},{"id":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"port":{"type":"number"}},"required":[],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx"},"routes":["tool:grok-bot/chatgpt_desktop_list_hosts"]},{"id":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_list_threads.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from the prior page; keep filters unchanged.","type":"string"},"groupBy":{"description":"When \"host\", also return groups[] keyed by host (grouping mode, not a host id).","enum":["host"],"type":"string"},"host":{"description":"Any string filter for machine/location. Reserved: \"all\" (default), \"local\". Otherwise a hostId or friendly name discovered at runtime — see chatgpt_desktop_list_hosts. Not an enum; new machines/connections appear with no code change.","type":"string"},"limit":{"default":50,"description":"Maximum threads per page.","type":"number"},"modelProvider":{"description":"Any modelProvider id string (passed through to app-server modelProviders). Omit for all providers. Discovered values: see chatgpt_desktop_list_hosts. Not an enum.","type":"string"},"port":{"type":"number"},"project":{"description":"Match a Desktop project label or app-server projectId. Dynamic string, not an enum.","type":"string"}},"required":[],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/chatgpt_desktop_list_threads.tsx"},"routes":["tool:grok-bot/chatgpt_desktop_list_threads"]},{"id":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_open_thread.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"openTimeoutMs":{"description":"Maximum time to wait for the requested conversation (1-600000 ms).","type":"number"},"port":{"description":"Local CDP port (default 9222 or CHATGPT_DESKTOP_CDP_PORT).","type":"number"},"threadId":{"description":"local: or bare durable id. Temporary local:client-new-thread:* works only while selected; remote ids require their owning host.","type":"string"}},"required":["threadId"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/chatgpt_desktop_open_thread.tsx"},"routes":["tool:grok-bot/chatgpt_desktop_open_thread"]},{"id":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_read_thread.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from the prior read page. Keep threadId and full unchanged.","type":"string"},"full":{"description":"When true, page from the start through the complete history (up to 2000 turns by default); otherwise return the most recent 100 turns by default. Explicit limit bounds either mode.","type":"boolean"},"limit":{"type":"number"},"openTimeoutMs":{"description":"Max wait for Loading task… to clear after opening (default 90000).","type":"number"},"port":{"type":"number"},"threadId":{"description":"local:, bare durable id, or selected local:client-new-thread:* temporary id. Remote ids return REMOTE_THREAD_NOT_LOADED with hostId.","type":"string"}},"required":["threadId"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/chatgpt_desktop_read_thread.tsx"},"routes":["tool:grok-bot/chatgpt_desktop_read_thread"]},{"id":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_search_threads.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from the previous search page; keep query and filters unchanged.","type":"string"},"groupBy":{"description":"When \"host\", also return groups[] keyed by host (grouping mode, not a host id).","enum":["host"],"type":"string"},"host":{"description":"Any string filter for machine/location. Reserved: \"all\" (default), \"local\". Otherwise a hostId or friendly name discovered at runtime — see chatgpt_desktop_list_hosts. Not an enum; new machines/connections appear with no code change.","type":"string"},"limit":{"default":50,"description":"Maximum matches.","type":"number"},"modelProvider":{"description":"Any modelProvider id string (passed through to app-server modelProviders). Omit for all providers. Discovered values: see chatgpt_desktop_list_hosts. Not an enum.","type":"string"},"port":{"type":"number"},"project":{"description":"Match a Desktop project label or app-server projectId.","type":"string"},"query":{"description":"Case-insensitive substring over thread metadata.","type":"string"}},"required":["query"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/chatgpt_desktop_search_threads.tsx"},"routes":["tool:grok-bot/chatgpt_desktop_search_threads"]},{"id":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_send.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"openTimeoutMs":{"type":"number"},"port":{"type":"number"},"project":{"description":"With threadId omitted, create the new chat inside this project. Omit for a new chat outside projects.","type":"string"},"text":{"type":"string"},"threadId":{"description":"local:, bare durable id, or selected local:client-new-thread:*; omit for a new chat.","type":"string"},"unarchive":{"default":false,"description":"Existing threadId only: explicitly call thread/unarchive after ARCHIVED_THREAD, then retry the send once. Never implicit.","type":"boolean"}},"required":["text"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/chatgpt_desktop_send.tsx"},"routes":["tool:grok-bot/chatgpt_desktop_send"]},{"id":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_status.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"port":{"description":"Local CDP port (default 9222 or CHATGPT_DESKTOP_CDP_PORT).","type":"number"}},"required":[],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/chatgpt_desktop_status.tsx"},"routes":["tool:grok-bot/chatgpt_desktop_status"]},{"id":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_wait_reply.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"port":{"type":"number"},"threadId":{"description":"Durable local:, bare id, or selected local:client-new-thread:* from send.","type":"string"},"timeoutMs":{"default":120000,"type":"number"}},"required":[],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/chatgpt_desktop_wait_reply.tsx"},"routes":["tool:grok-bot/chatgpt_desktop_wait_reply"]},{"id":"contract:src/mcp/grok-bot/tools/claude_send.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"message":{"type":"string"},"name":{"type":"string"},"timeoutMs":{"type":"number"}},"required":["name","message"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/claude_send.tsx"},"routes":["tool:grok-bot/claude_send"]},{"id":"contract:src/mcp/grok-bot/tools/codex_new.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"cwd":{"type":"string"},"effort":{"enum":["none","minimal","low","medium","high","xhigh"],"type":"string"},"expectedCwd":{"type":"string"},"message":{"type":"string"},"model":{"type":"string"}},"required":["cwd"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/codex_new.tsx"},"routes":["tool:grok-bot/codex_new"]},{"id":"contract:src/mcp/grok-bot/tools/codex_send.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"correlationId":{"type":"string"},"effort":{"enum":["none","minimal","low","medium","high","xhigh"],"type":"string"},"envelope":{"type":"boolean"},"expectedCwd":{"type":"string"},"expectedTurnId":{"type":"string"},"hop":{"type":"number"},"maxOutputBytes":{"type":"number"},"message":{"type":"string"},"model":{"type":"string"},"replyTo":{"type":"string"},"replyToGrok":{"type":"string"},"requestId":{"type":"string"},"threadId":{"type":"string"},"timeoutMs":{"type":"number"},"wait":{"default":false,"type":"boolean"},"whenBusy":{"enum":["reject","queue","steer"],"type":"string"}},"required":["threadId","message"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/codex_send.tsx"},"routes":["tool:grok-bot/codex_send"]},{"id":"contract:src/mcp/grok-bot/tools/codex_threads.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"activeWithin":{"description":"Only threads updated within this long: 90s, 30m, 12h, 7d, 2w.","type":"string"},"archived":{"description":"true = archived threads only.","type":"boolean"},"cursor":{"description":"Opaque nextCursor from a previous page.","type":"string"},"cwd":{"description":"Exact session working directory.","type":"string"},"limit":{"description":"Maximum threads in this page: 1-200 (default 20). Every provider is included; use cursor/nextCursor to page.","type":"number"},"modelProvider":{"description":"Any modelProvider id; omit for all.","type":"string"},"order":{"default":"desc","enum":["desc","asc"],"type":"string"},"query":{"description":"Case-insensitive substring of thread name/title, preview or id (id prefix works). Scans all pages.","type":"string"},"since":{"description":"Only threads updated at or after this ISO date/time or epoch seconds.","type":"string"},"sort":{"default":"updated","description":"updated = last activity (default), created = creation time, recency = daemon recency order.","enum":["updated","created","recency"],"type":"string"},"sourceKind":{"description":"Any app-server source kind (cli, vscode, exec, appServer, subAgent...); omit for interactive sources.","type":"string"}},"required":[],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/codex_threads.tsx"},"routes":["tool:grok-bot/codex_threads"]},{"id":"contract:src/mcp/grok-bot/tools/codex_wait.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"expectedCwd":{"type":"string"},"maxOutputBytes":{"description":"Reply budget: 1-4194304 bytes.","type":"number"},"messageId":{"type":"string"},"threadId":{"type":"string"},"timeoutMs":{"description":"Observation timeout: 1-7200000 milliseconds.","type":"number"},"turnId":{"type":"string"}},"required":["threadId","turnId"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/codex_wait.tsx"},"routes":["tool:grok-bot/codex_wait"]},{"id":"contract:src/mcp/grok-bot/tools/codex_watch.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"expectedCwd":{"type":"string"},"maxEvents":{"description":"Maximum observed events: 1-500.","type":"number"},"threadId":{"type":"string"},"timeoutMs":{"description":"Observation timeout: 1-600000 milliseconds.","type":"number"}},"required":["threadId"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/codex_watch.tsx"},"routes":["tool:grok-bot/codex_watch"]},{"id":"contract:src/mcp/grok-bot/tools/gbot_bridge_start.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"busyPolicy":{"enum":["steer","reject"],"type":"string"},"codexThreadId":{"type":"string"},"expectedCwd":{"type":"string"},"grokTarget":{"type":"string"},"requestId":{"type":"string"}},"required":["grokTarget"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/gbot_bridge_start.tsx"},"routes":["tool:grok-bot/gbot_bridge_start"]},{"id":"contract:src/mcp/grok-bot/tools/gbot_bridge_status.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"limit":{"type":"number"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/gbot_bridge_status.tsx"},"routes":["tool:grok-bot/gbot_bridge_status"]},{"id":"contract:src/mcp/grok-bot/tools/gbot_bridge_stop.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"all":{"type":"boolean"},"bindingId":{"type":"string"},"worker":{"type":"boolean"}},"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/gbot_bridge_stop.tsx"},"routes":["tool:grok-bot/gbot_bridge_stop"]},{"id":"contract:src/mcp/grok-bot/tools/gbot_codex_respond.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"answersJson":{"description":"JSON object mapping each advertised question ID to {\"answers\":[\"answer\"]}; exact IDs required.","type":"string"},"bindingId":{"type":"string"},"decision":{"enum":["accept","decline","cancel"],"type":"string"},"exchangeId":{"type":"string"},"generation":{"type":"string"},"interactionId":{"type":"string"},"threadId":{"type":"string"},"turnId":{"type":"string"}},"required":["interactionId","generation","threadId","turnId"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/gbot_codex_respond.tsx"},"routes":["tool:grok-bot/gbot_codex_respond"]},{"id":"contract:src/mcp/grok-bot/tools/gbot_grok_approvals.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"target":{"type":"string"}},"required":["target"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/gbot_grok_approvals.tsx"},"routes":["tool:grok-bot/gbot_grok_approvals"]},{"id":"contract:src/mcp/grok-bot/tools/gbot_grok_respond.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"decision":{"enum":["accept","decline"],"type":"string"},"entryId":{"type":"string"},"requestId":{"type":"string"},"target":{"type":"string"}},"required":["target","entryId","requestId","decision"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/gbot_grok_respond.tsx"},"routes":["tool:grok-bot/gbot_grok_respond"]},{"id":"contract:src/mcp/grok-bot/tools/gbot_send.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"codexThreadId":{"type":"string"},"correlationId":{"type":"string"},"expectedCwd":{"type":"string"},"hop":{"description":"Explicit chain hop count, refused at the configured bound.","type":"number"},"message":{"type":"string"},"replyMode":{"enum":["auto","manual"],"type":"string"},"requestId":{"type":"string"},"target":{"type":"string"}},"required":["target","message"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/gbot_send.tsx"},"routes":["tool:grok-bot/gbot_send"]},{"id":"contract:src/mcp/grok-bot/tools/gbot_thread.tsx#inputJsonSchema","input":{"additionalProperties":false,"properties":{"after":{"description":"Opaque cursor from the previous call. Returns entries strictly after it; an unknown cursor resets with a bounded snapshot.","type":"string"},"full":{"default":false,"description":"Return entries with text up to bounded budgets (20k chars per entry, 200k total). Every entry reports truncated/fullLength; read any remainder with `gbot thread --full` / `--json` on the machine.","type":"boolean"},"limit":{"default":40,"description":"How many trailing entries to inspect (1-200). Entries are returned only with full:true.","type":"number"},"target":{"description":"Bot or group name or id, for example \"General\".","type":"string"}},"required":["target"],"type":"object"},"origin":{"binding":"inputJsonSchema","module":"src/mcp/grok-bot/tools/gbot_thread.tsx"},"routes":["tool:grok-bot/gbot_thread"]}],"digest":"bc29982af3710b2b65dd33190d3d1c7f8a92e2425e9d015d74e0cf698ceca8c2","events":[],"layouts":[],"providers":[],"scripts":[{"id":"script:gbot-relay","kind":"script","provenance":{"kind":"conventional"},"source":"src/scripts/gbot-relay.ts"}],"servers":[{"id":"mcp:grok-bot","mode":"generated","name":"grok-bot","routes":[{"contract":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx#inputJsonSchema","description":"Discover local, remote-control environment ids, managed SSH hosts, and modelProvider ids. hostName is null when Desktop has no real display name. Use hostId or label in list/search filters; remote thread ids must be read on the owning host. warnings report provider discovery failures.","id":"tool:grok-bot/chatgpt_desktop_list_hosts","inputSchema":{"additionalProperties":false,"properties":{"port":{"type":"number"}},"required":[],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/chatgpt_desktop_list_hosts.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_list_threads.tsx#inputJsonSchema","description":"List one sorted Desktop inventory without duplicate rows. Pass nextCursor as cursor with unchanged filters until null; pages fill limit unless the MCP byte budget binds. A changed/expired inventory cursor requires restarting. Display titles are capped at 200 characters with titleTruncated:true. Filter by dynamic hostId/hostName, modelProvider, or Desktop project label/projectId; projectRootPath identifies the resolved workspace. Rows expose local:, location, hostId, and provider; remote rows require their owning host for reading or sending. Backend and warnings identify partial sources.","id":"tool:grok-bot/chatgpt_desktop_list_threads","inputSchema":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from the prior page; keep filters unchanged.","type":"string"},"groupBy":{"description":"When \"host\", also return groups[] keyed by host (grouping mode, not a host id).","enum":["host"],"type":"string"},"host":{"description":"Any string filter for machine/location. Reserved: \"all\" (default), \"local\". Otherwise a hostId or friendly name discovered at runtime — see chatgpt_desktop_list_hosts. Not an enum; new machines/connections appear with no code change.","type":"string"},"limit":{"default":50,"description":"Maximum threads per page.","type":"number"},"modelProvider":{"description":"Any modelProvider id string (passed through to app-server modelProviders). Omit for all providers. Discovered values: see chatgpt_desktop_list_hosts. Not an enum.","type":"string"},"port":{"type":"number"},"project":{"description":"Match a Desktop project label or app-server projectId. Dynamic string, not an enum.","type":"string"}},"required":[],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/chatgpt_desktop_list_threads.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_open_thread.tsx#inputJsonSchema","description":"Navigate Desktop to an existing local: or bare conversation id, even when the row is absent from the sidebar. Archived routes return archived:true without unarchiving; read them with chatgpt_desktop_read_thread. A remote-control id is owned by its hostId and cannot be opened on this Mac. CDP_UNREACHABLE means Desktop must be reachable locally.","id":"tool:grok-bot/chatgpt_desktop_open_thread","inputSchema":{"additionalProperties":false,"properties":{"openTimeoutMs":{"description":"Maximum time to wait for the requested conversation (1-600000 ms).","type":"number"},"port":{"description":"Local CDP port (default 9222 or CHATGPT_DESKTOP_CDP_PORT).","type":"number"},"threadId":{"description":"local: or bare durable id. Temporary local:client-new-thread:* works only while selected; remote ids require their owning host.","type":"string"}},"required":["threadId"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/chatgpt_desktop_open_thread.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_read_thread.tsx#inputJsonSchema","description":"Read a local:, bare durable id, or selected temporary local:client-new-thread:* id. Pass nextCursor back as cursor with the same threadId/full mode until complete:true. Pages stop at a byte budget; oversized turns use ordered continuation fragments with stable turnKey and field offsets, without dropping text. Archived threads remain readable. Warnings explain partial pages or app-server errors. REMOTE_THREAD_NOT_LOADED includes the owning hostId: call on that host.","id":"tool:grok-bot/chatgpt_desktop_read_thread","inputSchema":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from the prior read page. Keep threadId and full unchanged.","type":"string"},"full":{"description":"When true, page from the start through the complete history (up to 2000 turns by default); otherwise return the most recent 100 turns by default. Explicit limit bounds either mode.","type":"boolean"},"limit":{"type":"number"},"openTimeoutMs":{"description":"Max wait for Loading task… to clear after opening (default 90000).","type":"number"},"port":{"type":"number"},"threadId":{"description":"local:, bare durable id, or selected local:client-new-thread:* temporary id. Remote ids return REMOTE_THREAD_NOT_LOADED with hostId.","type":"string"}},"required":["threadId"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/chatgpt_desktop_read_thread.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_search_threads.tsx#inputJsonSchema","description":"Search full Desktop thread metadata by case-insensitive substring. Display titles are capped at 200 characters with titleTruncated:true. Pass nextCursor as cursor with unchanged query and filters to retrieve all matches. Dynamic hostId/hostName, modelProvider, and project filters apply. Remote results identify hostId and cannot be read on this Mac.","id":"tool:grok-bot/chatgpt_desktop_search_threads","inputSchema":{"additionalProperties":false,"properties":{"cursor":{"description":"Opaque nextCursor from the previous search page; keep query and filters unchanged.","type":"string"},"groupBy":{"description":"When \"host\", also return groups[] keyed by host (grouping mode, not a host id).","enum":["host"],"type":"string"},"host":{"description":"Any string filter for machine/location. Reserved: \"all\" (default), \"local\". Otherwise a hostId or friendly name discovered at runtime — see chatgpt_desktop_list_hosts. Not an enum; new machines/connections appear with no code change.","type":"string"},"limit":{"default":50,"description":"Maximum matches.","type":"number"},"modelProvider":{"description":"Any modelProvider id string (passed through to app-server modelProviders). Omit for all providers. Discovered values: see chatgpt_desktop_list_hosts. Not an enum.","type":"string"},"port":{"type":"number"},"project":{"description":"Match a Desktop project label or app-server projectId.","type":"string"},"query":{"description":"Case-insensitive substring over thread metadata.","type":"string"}},"required":["query"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/chatgpt_desktop_search_threads.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_send.tsx#inputJsonSchema","description":"Send once through local Desktop CDP. With threadId, accept local:, bare durable id, or currently selected local:client-new-thread:*; remote ids must be sent on their hostId. Omit threadId and project for a new chat outside projects; omit threadId and set project for a new chat inside it. The empty view and project scope are checked before typing. New-thread receipts include temporaryThreadId when seen and return durable local: once resolved. ARCHIVED_THREAD rejects without sending; unarchive:true explicitly unarchives that existing thread first. PROJECT_UNAVAILABLE identifies a disabled project action or missing configured root; NEW_CHAT_NAVIGATION_FAILED reports an unchanged view promptly. CDP_UNREACHABLE and COMPOSER_HAS_DRAFT require operator inspection, not blind retry.","id":"tool:grok-bot/chatgpt_desktop_send","inputSchema":{"additionalProperties":false,"properties":{"openTimeoutMs":{"type":"number"},"port":{"type":"number"},"project":{"description":"With threadId omitted, create the new chat inside this project. Omit for a new chat outside projects.","type":"string"},"text":{"type":"string"},"threadId":{"description":"local:, bare durable id, or selected local:client-new-thread:*; omit for a new chat.","type":"string"},"unarchive":{"default":false,"description":"Existing threadId only: explicitly call thread/unarchive after ARCHIVED_THREAD, then retry the send once. Never implicit.","type":"boolean"}},"required":["text"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/chatgpt_desktop_send.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_status.tsx#inputJsonSchema","description":"Check local ChatGPT Desktop CDP before send, wait, or open. reachable:false and exitCode:1 mean CDP is down even if appServerFallback.reachable is true; list/search/read may still work through app-server. There is no remote CDP transport.","id":"tool:grok-bot/chatgpt_desktop_status","inputSchema":{"additionalProperties":false,"properties":{"port":{"description":"Local CDP port (default 9222 or CHATGPT_DESKTOP_CDP_PORT).","type":"number"}},"required":[],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/chatgpt_desktop_status.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/chatgpt_desktop_wait_reply.tsx#inputJsonSchema","description":"After chatgpt_desktop_send, wait for the selected Desktop reply over CDP. Accept the returned durable local: or the temporary local:client-new-thread:* while selected; omit threadId only when staying on the same selected chat. Returns reply, durable threadId and conversationId. timeout is not proof the send failed; CDP_UNREACHABLE requires Desktop recovery. Remote ids must be handled on their hostId.","id":"tool:grok-bot/chatgpt_desktop_wait_reply","inputSchema":{"additionalProperties":false,"properties":{"port":{"type":"number"},"threadId":{"description":"Durable local:, bare id, or selected local:client-new-thread:* from send.","type":"string"},"timeoutMs":{"default":120000,"type":"number"}},"required":[],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/chatgpt_desktop_wait_reply.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/claude_send.tsx#inputJsonSchema","description":"Send to a named, opted-in live Claude Code channel and wait for its reply. Unknown delivery must not be retried automatically. Runs on the user's registered machine via its local socket, not the Grok Bot box. From the box, use Grok Bot Shell with a machineId to run gbot there after codex app-server daemon start or bootstrap; gbot has no remote transport.","id":"tool:grok-bot/claude_send","inputSchema":{"additionalProperties":false,"properties":{"message":{"type":"string"},"name":{"type":"string"},"timeoutMs":{"type":"number"}},"required":["name","message"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/claude_send.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/codex_new.tsx#inputJsonSchema","description":"Start a new Codex thread on this machine in cwd, optionally sending its first message. Codex must run locally on the same machine as gbot.","id":"tool:grok-bot/codex_new","inputSchema":{"additionalProperties":false,"properties":{"cwd":{"type":"string"},"effort":{"enum":["none","minimal","low","medium","high","xhigh"],"type":"string"},"expectedCwd":{"type":"string"},"message":{"type":"string"},"model":{"type":"string"}},"required":["cwd"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/codex_new.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/codex_send.tsx#inputJsonSchema","description":"Send to a Codex thread. Runs on the user's registered machine via its local socket, not the Grok Bot box. From the box, use Grok Bot Shell with a machineId to run gbot there after codex app-server daemon start or bootstrap; gbot has no remote transport. With replyToGrok or bindingId, managed delivery returns the terminal answer to Grok automatically. Otherwise optional wait observes completion and explicit steer requires expectedTurnId. Acceptance is not completion.","id":"tool:grok-bot/codex_send","inputSchema":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"correlationId":{"type":"string"},"effort":{"enum":["none","minimal","low","medium","high","xhigh"],"type":"string"},"envelope":{"type":"boolean"},"expectedCwd":{"type":"string"},"expectedTurnId":{"type":"string"},"hop":{"type":"number"},"maxOutputBytes":{"type":"number"},"message":{"type":"string"},"model":{"type":"string"},"replyTo":{"type":"string"},"replyToGrok":{"type":"string"},"requestId":{"type":"string"},"threadId":{"type":"string"},"timeoutMs":{"type":"number"},"wait":{"default":false,"type":"boolean"},"whenBusy":{"enum":["reject","queue","steer"],"type":"string"}},"required":["threadId","message"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/codex_send.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/codex_threads.tsx#inputJsonSchema","description":"Discover Codex daemon threads on the user's registered machine (local socket, not the Grok Bot box; from the box run gbot there through Shell with a machineId after codex app-server daemon start or bootstrap). Newest activity first by default (sort:\"updated\"), so old-but-active threads surface; sort:\"created\" restores creation order. Find one thread with query (case-insensitive substring of name/title, preview or id, so an id prefix works) - it scans every page, not just the first 100. Narrow with activeWithin (\"30m\", \"12h\", \"7d\", \"2w\") or since (ISO/epoch seconds), cwd, modelProvider, sourceKind, archived. limit is the page size (1-200); pass nextCursor back as cursor for more.","id":"tool:grok-bot/codex_threads","inputSchema":{"additionalProperties":false,"properties":{"activeWithin":{"description":"Only threads updated within this long: 90s, 30m, 12h, 7d, 2w.","type":"string"},"archived":{"description":"true = archived threads only.","type":"boolean"},"cursor":{"description":"Opaque nextCursor from a previous page.","type":"string"},"cwd":{"description":"Exact session working directory.","type":"string"},"limit":{"description":"Maximum threads in this page: 1-200 (default 20). Every provider is included; use cursor/nextCursor to page.","type":"number"},"modelProvider":{"description":"Any modelProvider id; omit for all.","type":"string"},"order":{"default":"desc","enum":["desc","asc"],"type":"string"},"query":{"description":"Case-insensitive substring of thread name/title, preview or id (id prefix works). Scans all pages.","type":"string"},"since":{"description":"Only threads updated at or after this ISO date/time or epoch seconds.","type":"string"},"sort":{"default":"updated","description":"updated = last activity (default), created = creation time, recency = daemon recency order.","enum":["updated","created","recency"],"type":"string"},"sourceKind":{"description":"Any app-server source kind (cli, vscode, exec, appServer, subAgent...); omit for interactive sources.","type":"string"}},"required":[],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/codex_threads.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/codex_wait.tsx#inputJsonSchema","description":"Explicit diagnostic observation of one Codex turn; returns execution and final reply without interrupting it. Runs on the user's registered machine via its local socket, not the Grok Bot box. From the box, use Grok Bot Shell with a machineId to run gbot there after codex app-server daemon start or bootstrap; gbot has no remote transport.","id":"tool:grok-bot/codex_wait","inputSchema":{"additionalProperties":false,"properties":{"expectedCwd":{"type":"string"},"maxOutputBytes":{"description":"Reply budget: 1-4194304 bytes.","type":"number"},"messageId":{"type":"string"},"threadId":{"type":"string"},"timeoutMs":{"description":"Observation timeout: 1-7200000 milliseconds.","type":"number"},"turnId":{"type":"string"}},"required":["threadId","turnId"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/codex_wait.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/codex_watch.tsx#inputJsonSchema","description":"Watch bounded Codex thread events for diagnostics without answering approvals. Runs on the user's registered machine via its local socket, not the Grok Bot box. From the box, use Grok Bot Shell with a machineId to run gbot there after codex app-server daemon start or bootstrap; gbot has no remote transport.","id":"tool:grok-bot/codex_watch","inputSchema":{"additionalProperties":false,"properties":{"expectedCwd":{"type":"string"},"maxEvents":{"description":"Maximum observed events: 1-500.","type":"number"},"threadId":{"type":"string"},"timeoutMs":{"description":"Observation timeout: 1-600000 milliseconds.","type":"number"}},"required":["threadId"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/codex_watch.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/gbot_bridge_start.tsx#inputJsonSchema","description":"Link a Grok conversation to Codex once. New visible Grok messages arrive automatically and Codex final answers return to Grok.","id":"tool:grok-bot/gbot_bridge_start","inputSchema":{"additionalProperties":false,"properties":{"busyPolicy":{"enum":["steer","reject"],"type":"string"},"codexThreadId":{"type":"string"},"expectedCwd":{"type":"string"},"grokTarget":{"type":"string"},"requestId":{"type":"string"}},"required":["grokTarget"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/gbot_bridge_start.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/gbot_bridge_status.tsx#inputJsonSchema","description":"Inspect worker health, route coverage, bounded receipts and scoped pending operator interactions.","id":"tool:grok-bot/gbot_bridge_status","inputSchema":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"limit":{"type":"number"}},"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/gbot_bridge_status.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/gbot_bridge_stop.tsx#inputJsonSchema","description":"Stop a binding without deleting receipts or interrupting Codex. all stops all bindings; worker explicitly shuts down the worker.","id":"tool:grok-bot/gbot_bridge_stop","inputSchema":{"additionalProperties":false,"properties":{"all":{"type":"boolean"},"bindingId":{"type":"string"},"worker":{"type":"boolean"}},"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/gbot_bridge_stop.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/gbot_codex_respond.tsx#inputJsonSchema","description":"Explicit operator response to a current scoped Codex interaction. Supports only one-time accept/decline/cancel or exact question-ID answers. Never auto-approve.","id":"tool:grok-bot/gbot_codex_respond","inputSchema":{"additionalProperties":false,"properties":{"answersJson":{"description":"JSON object mapping each advertised question ID to {\"answers\":[\"answer\"]}; exact IDs required.","type":"string"},"bindingId":{"type":"string"},"decision":{"enum":["accept","decline","cancel"],"type":"string"},"exchangeId":{"type":"string"},"generation":{"type":"string"},"interactionId":{"type":"string"},"threadId":{"type":"string"},"turnId":{"type":"string"}},"required":["interactionId","generation","threadId","turnId"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/gbot_codex_respond.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/gbot_grok_approvals.tsx#inputJsonSchema","description":"List pending auto-review and local-tool approval cards in the latest 200 entries for a Grok bot. Older or unsupported requests require the owning Grok UI.","id":"tool:grok-bot/gbot_grok_approvals","inputSchema":{"additionalProperties":false,"properties":{"target":{"type":"string"}},"required":["target"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/gbot_grok_approvals.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/gbot_grok_respond.tsx#inputJsonSchema","description":"Only after an explicit user decision: accept one current Grok approval once or decline it. Exact target, entryId and approval requestId required. Never auto-approve or grant persistent permissions. Success acknowledges response delivery, not execution.","id":"tool:grok-bot/gbot_grok_respond","inputSchema":{"additionalProperties":false,"properties":{"decision":{"enum":["accept","decline"],"type":"string"},"entryId":{"type":"string"},"requestId":{"type":"string"},"target":{"type":"string"}},"required":["target","entryId","requestId","decision"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/gbot_grok_respond.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/gbot_send.tsx#inputJsonSchema","description":"Send to Grok Bot. Native Codex calls automatically receive replies in their originating thread; send once and continue work. Without a native source, supply codexThreadId or use manual gbot_thread reading.","id":"tool:grok-bot/gbot_send","inputSchema":{"additionalProperties":false,"properties":{"bindingId":{"type":"string"},"codexThreadId":{"type":"string"},"correlationId":{"type":"string"},"expectedCwd":{"type":"string"},"hop":{"description":"Explicit chain hop count, refused at the configured bound.","type":"number"},"message":{"type":"string"},"replyMode":{"enum":["auto","manual"],"type":"string"},"requestId":{"type":"string"},"target":{"type":"string"}},"required":["target","message"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/gbot_send.tsx"},{"contract":"contract:src/mcp/grok-bot/tools/gbot_thread.tsx#inputJsonSchema","description":"Read a bounded Grok Bot thread tail. Returns a small receipt by default; pass the last cursor as after for an exclusive client-side delta, or full:true to include bounded entry text.","id":"tool:grok-bot/gbot_thread","inputSchema":{"additionalProperties":false,"properties":{"after":{"description":"Opaque cursor from the previous call. Returns entries strictly after it; an unknown cursor resets with a bounded snapshot.","type":"string"},"full":{"default":false,"description":"Return entries with text up to bounded budgets (20k chars per entry, 200k total). Every entry reports truncated/fullLength; read any remainder with `gbot thread --full` / `--json` on the machine.","type":"boolean"},"limit":{"default":40,"description":"How many trailing entries to inspect (1-200). Entries are returned only with full:true.","type":"number"},"target":{"description":"Bot or group name or id, for example \"General\".","type":"string"}},"required":["target"],"type":"object"},"kind":"tool","provenance":{"kind":"conventional"},"serverId":"mcp:grok-bot","source":"src/mcp/grok-bot/tools/gbot_thread.tsx"}]}]},"runtime":{"node":"22.19.0"}} diff --git a/artifact/bin/gbot-flight.mjs b/artifact/bin/gbot-flight.mjs index 4b51e37..8bf56b4 100644 --- a/artifact/bin/gbot-flight.mjs +++ b/artifact/bin/gbot-flight.mjs @@ -28776,7 +28776,7 @@ var __webpack_modules__ = { }); }, "./src/core/desktop-shim-bridge.js" (__unused_rspack___webpack_module__, __webpack_exports__, __webpack_require__) { - const BRIDGE_SOURCE = "#!/usr/bin/env python3\n\"\"\"Stdio JSONL (Desktop) <-> WebSocket-over-unix (managed app-server daemon).\nReversible: remove CODEX_CLI_PATH wrapper. Does not read Desktop private pipes.\n\"\"\"\nfrom __future__ import annotations\nimport base64, hashlib, json, os, select, socket, struct, sys, threading, time\n\nSOCK = os.environ.get(\n \"CODEX_APP_SERVER_SOCK\",\n os.path.expanduser(\"~/.codex/app-server-control/app-server-control.sock\"),\n)\nLOG = os.environ.get(\"CODEX_STDIO_BRIDGE_LOG\", os.path.expanduser(\"~/.codex/bin/codex-stdio-to-daemon-ws.log\"))\nWS_GUID = \"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\"\n\n# Wrapper contract: 0 served the session, 1 failed while stdio was still\n# pristine -- no stdin byte consumed and no stdout frame committed (wrapper\n# falls back to real Codex), 2 failed mid-session (wrapper exits promptly so\n# Desktop reconnects; the fallback must never run on half-consumed stdin or\n# dirty stdout).\nEXIT_SERVED = 0\nEXIT_PRE_STDIO = 1\nEXIT_MID_SESSION = 2\n\nCONNECT_TIMEOUT_S = float(os.environ.get(\"CODEX_BRIDGE_CONNECT_TIMEOUT\", \"10\"))\n# Bound from connect to the matching first daemon response: a daemon that\n# completes the upgrade but never answers the first RPC must not hold Desktop\n# past this. Only the matching initialize response/error (or the deadline\n# itself) clears it; notifications never do.\nFIRST_MESSAGE_TIMEOUT_S = float(os.environ.get(\"CODEX_BRIDGE_FIRST_MESSAGE_TIMEOUT\", \"30\"))\n# Bound on every mid-session socket send and stdout write: a wedged or\n# non-reading peer must not hang Desktop past this. Reads after a successful\n# init block with no timeout so healthy idle sessions survive silence.\nIO_TIMEOUT_S = float(os.environ.get(\"CODEX_BRIDGE_IO_TIMEOUT\", \"30\"))\n\ndef log(msg: str) -> None:\n try:\n with open(LOG, \"a\") as f:\n f.write(time.strftime(\"%Y-%m-%dT%H:%M:%SZ\", time.gmtime()) + \" \" + msg + \"\\n\")\n except OSError:\n pass\n\ndef ws_connect(path: str, timeout: float = CONNECT_TIMEOUT_S):\n \"\"\"Connect + HTTP upgrade under one absolute monotonic deadline.\n\n Returns (socket, trailing) where trailing holds bytes coalesced after the\n upgrade headers (TCP may deliver the 101 and the first WS frame together;\n discarding them would lose the first message). Requires HTTP/1.1 101 plus\n a matching Sec-WebSocket-Accept so a 200 (or any non-101) fails fast even\n when the accept hash happens to be valid.\n \"\"\"\n deadline = time.monotonic() + timeout\n s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)\n s.settimeout(timeout)\n try:\n s.connect(path)\n key = base64.b64encode(os.urandom(16)).decode()\n expected = base64.b64encode(hashlib.sha1((key + WS_GUID).encode()).digest()).decode()\n req = (\n f\"GET /rpc HTTP/1.1\\r\\nHost: localhost\\r\\nUpgrade: websocket\\r\\n\"\n f\"Connection: Upgrade\\r\\nSec-WebSocket-Key: {key}\\r\\nSec-WebSocket-Version: 13\\r\\n\\r\\n\"\n ).encode()\n s.settimeout(max(0.0, deadline - time.monotonic()))\n s.sendall(req)\n buf = b\"\"\n while True:\n if b\"\\r\\n\\r\\n\" in buf:\n break\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"WebSocket upgrade timed out\")\n if len(buf) > 65536:\n raise ConnectionError(\"WS upgrade headers too large\")\n s.settimeout(remaining)\n chunk = s.recv(4096)\n if not chunk:\n raise ConnectionError(\"daemon closed during WS upgrade\")\n buf += chunk\n head, _, trailing = buf.partition(b\"\\r\\n\\r\\n\")\n lines = head.split(b\"\\r\\n\")\n if not lines or lines[0].split(b\" \", 2)[:2] != [b\"HTTP/1.1\", b\"101\"]:\n raise ConnectionError(f\"WS upgrade failed: {head[:200]!r}\")\n accept = None\n for line in lines[1:]:\n name, sep, value = line.partition(b\":\")\n if sep and name.strip().lower() == b\"sec-websocket-accept\":\n accept = value.strip().decode(\"latin-1\")\n if accept != expected:\n raise ConnectionError(\"WS upgrade accept mismatch\")\n except Exception:\n try:\n s.close()\n except OSError:\n pass\n raise\n # Keep one socket mode across reader and writer threads. Reads wait\n # indefinitely in select; sends use their own absolute deadline.\n s.setblocking(False)\n return s, trailing\n\ndef mask_send(sock: socket.socket, payload: bytes, opcode: int = 1, deadline=None) -> None:\n mask = os.urandom(4)\n ln = len(payload)\n if ln < 126:\n hdr = bytes([0x80 | opcode, 0x80 | ln])\n elif ln < 65536:\n hdr = bytes([0x80 | opcode, 0x80 | 126]) + struct.pack(\"!H\", ln)\n else:\n hdr = bytes([0x80 | opcode, 0x80 | 127]) + struct.pack(\"!Q\", ln)\n view = memoryview(hdr + mask + bytes(b ^ mask[i % 4] for i, b in enumerate(payload)))\n while view:\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"socket write timed out\")\n _, writable, _ = select.select([], [sock], [], remaining)\n if not writable:\n raise TimeoutError(\"socket write timed out\")\n try:\n n = sock.send(view)\n except BlockingIOError:\n continue\n if not n:\n raise ConnectionError(\"socket closed during write\")\n view = view[n:]\n\ndef locked_send(sock: socket.socket, lock: threading.Lock, payload: bytes, opcode: int = 1, timeout: float = CONNECT_TIMEOUT_S) -> None:\n \"\"\"Serialized send sharing one monotonic budget across lock wait + send.\n\n A non-reading peer must never wedge pong writes or timeout cleanup: on\n expiry raise TimeoutError so the caller closes and exits without hanging.\n \"\"\"\n if timeout is None:\n timeout = CONNECT_TIMEOUT_S\n deadline = time.monotonic() + timeout\n if timeout <= 0:\n raise TimeoutError(\"send budget expired\")\n if not lock.acquire(timeout=timeout):\n raise TimeoutError(\"send lock timed out\")\n try:\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"send budget expired\")\n mask_send(sock, payload, opcode=opcode, deadline=deadline)\n finally:\n lock.release()\n\ndef _buffered_reader(sock: socket.socket, initial: bytes = b\"\"):\n buf = bytearray(initial)\n def recv(n):\n while len(buf) < n:\n select.select([sock], [], [])\n try:\n chunk = sock.recv(n - len(buf))\n except BlockingIOError:\n continue\n if not chunk:\n raise ConnectionError(\"eof\")\n buf.extend(chunk)\n out = bytes(buf[:n])\n del buf[:n]\n return out\n return recv\n\ndef read_frame(recv):\n hdr = recv(2)\n fin = bool(hdr[0] & 0x80)\n opcode = hdr[0] & 0x0F\n masked = bool(hdr[1] & 0x80)\n ln = hdr[1] & 0x7F\n if ln == 126:\n ln = struct.unpack(\"!H\", recv(2))[0]\n elif ln == 127:\n ln = struct.unpack(\"!Q\", recv(8))[0]\n mask = recv(4) if masked else b\"\"\n payload = recv(ln)\n if masked:\n payload = bytes(b ^ mask[i % 4] for i, b in enumerate(payload))\n return fin, opcode, payload\n\ndef read_message(recv, send_pong):\n \"\"\"Next complete data message, assembling continuation frames.\n\n Control frames are handled inline (pong answered, close reported) so a\n fragmented message split around a ping still reassembles.\n \"\"\"\n opcode = None\n parts = []\n while True:\n fin, op, payload = read_frame(recv)\n if op == 0x8:\n return (\"close\", None, b\"\")\n if op == 0x9:\n send_pong(payload)\n continue\n if op == 0xA:\n continue\n if op in (0x1, 0x2):\n if opcode is not None:\n raise ConnectionError(\"data frame inside fragmented message\")\n if fin:\n return (\"data\", op, payload)\n opcode = op\n parts.append(payload)\n continue\n if op == 0x0:\n if opcode is None:\n raise ConnectionError(\"stray continuation frame\")\n parts.append(payload)\n if fin:\n return (\"data\", opcode, b\"\".join(parts))\n continue\n raise ConnectionError(f\"unsupported opcode {op}\")\n\ndef _is_daemon_response(payload: bytes, expect_id=None, known_ids=None) -> bool:\n \"\"\"True only for the matching JSON-RPC response/error, never a notification.\n\n The first-RPC timer must survive unrelated daemon notifications and foreign\n responses: only the response/error whose id matches the client's initialize\n (or, before it is known, any id the client already sent) proves the first\n RPC got an answer. Timeouts clear it via the deadline, never via data.\n \"\"\"\n try:\n text = payload.decode(\"utf-8\").strip()\n msg = json.loads(text)\n except Exception:\n return False\n if not isinstance(msg, dict):\n return False\n rid = msg.get(\"id\")\n if rid is None or \"method\" in msg or (\"result\" not in msg and \"error\" not in msg):\n return False\n if expect_id is not None:\n return rid == expect_id\n if known_ids:\n return rid in known_ids\n return False\n\ndef _bounded_stdout_write(data: bytes, timeout: float = IO_TIMEOUT_S) -> None:\n \"\"\"Write one daemon frame to stdout without hanging on a non-reading peer.\n\n Waits for writability under the mid-session write budget and writes via\n os.write in a loop, so a Desktop that stopped reading cannot hang the\n bridge and then exit 1 on dirty stdout.\n \"\"\"\n if timeout is None:\n timeout = IO_TIMEOUT_S\n if timeout <= 0:\n raise TimeoutError(\"stdout budget expired\")\n fd = sys.stdout.fileno()\n deadline = time.monotonic() + timeout\n was_blocking = os.get_blocking(fd)\n os.set_blocking(fd, False)\n try:\n view = memoryview(data)\n while view:\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"stdout write timed out\")\n _, writable, _ = select.select([], [fd], [], remaining)\n if not writable:\n raise TimeoutError(\"stdout write timed out\")\n try:\n n = os.write(fd, view)\n except BlockingIOError:\n continue\n view = view[n:]\n finally:\n os.set_blocking(fd, was_blocking)\n\ndef stdout_writer(sock: socket.socket, recv, send_pong, done: threading.Event, first_msg: threading.Event, output_forwarded: threading.Event, client_ids=None, init_state=None) -> None:\n try:\n while True:\n kind, opcode, payload = read_message(recv, send_pong)\n if kind == \"close\":\n log(\"daemon WS close\")\n return\n if opcode == 0x1:\n # Desktop StdioConnection expects newline-delimited JSON text\n if not payload.endswith(b\"\\n\"):\n payload += b\"\\n\"\n # Commit BEFORE writing: once a daemon byte is about to hit\n # stdout, the session is dirty and the fallback must never run,\n # even if the write itself blocks or fails.\n output_forwarded.set()\n _bounded_stdout_write(payload)\n if opcode == 0x1:\n expect = (init_state or {}).get(\"initialize_id\") if isinstance(init_state, dict) else None\n if _is_daemon_response(payload, expect, client_ids or ()):\n first_msg.set()\n except Exception as e:\n log(f\"stdout_writer exit: {e}\")\n finally:\n done.set()\n\ndef main() -> int:\n log(f\"start sock={SOCK}\")\n try:\n sock, pending = ws_connect(SOCK)\n except Exception as e:\n log(f\"connect failed: {e}\")\n sys.stderr.write(f\"codex-stdio-to-daemon-ws: {e}\\n\")\n return EXIT_PRE_STDIO\n log(\"connected\")\n connected_at = time.monotonic()\n first_deadline = connected_at + FIRST_MESSAGE_TIMEOUT_S\n send_lock = threading.Lock()\n done = threading.Event()\n first_msg = threading.Event()\n output_forwarded = threading.Event()\n client_ids = set()\n init_state = {\"initialize_id\": None}\n\n def _send_timeout() -> float:\n # Before the first matching daemon response the remaining first-RPC\n # budget bounds every send; after it each send gets the mid-session\n # write budget. Idle reads are never timed out.\n if not first_msg.is_set():\n return max(0.0, first_deadline - time.monotonic())\n return IO_TIMEOUT_S\n\n def _cleanup_timeout() -> float:\n # Timeout cleanup stays within the expired session budget: once the\n # first-RPC deadline has passed, close directly instead of opening\n # fresh windows that let a short budget take several timeout periods.\n if not first_msg.is_set():\n return max(0.0, first_deadline - time.monotonic())\n return CONNECT_TIMEOUT_S\n\n def send_pong(payload: bytes) -> None:\n locked_send(sock, send_lock, payload, opcode=0xA, timeout=_send_timeout())\n recv = _buffered_reader(sock, pending)\n t = threading.Thread(target=stdout_writer, args=(sock, recv, send_pong, done, first_msg, output_forwarded, client_ids, init_state), daemon=True)\n t.start()\n # Byte-precise commit point: the fallback may run only while zero stdin\n # bytes have left the pipe and no daemon payload was committed to stdout.\n # Reads use os.read into a userspace line buffer (buffered readline could\n # readahead past the commit point), and every byte read counts -- even a\n # blank line. first_msg tracks the matching daemon response/error, never\n # a notification.\n stdin_bytes = 0\n pending_in = b\"\"\n clean_eof = False\n try:\n fd = sys.stdin.fileno()\n while not done.is_set():\n if not first_msg.is_set() and time.monotonic() >= first_deadline:\n log(\"first daemon message timed out\")\n break\n ready, _, _ = select.select([fd], [], [], 0.5)\n if not ready:\n continue\n chunk = os.read(fd, 65536)\n if not chunk:\n clean_eof = True\n break\n stdin_bytes += len(chunk)\n pending_in += chunk\n while b\"\\n\" in pending_in:\n line, _, pending_in = pending_in.partition(b\"\\n\")\n line = line.strip()\n if not line:\n continue\n try:\n try:\n obj = json.loads(line.decode(\"utf-8\"))\n except Exception:\n obj = None\n if isinstance(obj, dict) and obj.get(\"id\") is not None:\n client_ids.add(obj.get(\"id\"))\n if obj.get(\"method\") == \"initialize\" and init_state[\"initialize_id\"] is None:\n init_state[\"initialize_id\"] = obj.get(\"id\")\n except Exception:\n pass\n try:\n locked_send(sock, send_lock, line, timeout=_send_timeout())\n except TimeoutError as e:\n log(f\"stdin forward timed out: {e}\")\n break\n except Exception as e:\n log(f\"stdin forward failed: {e}\")\n break\n else:\n continue\n break\n except Exception as e:\n log(f\"stdin loop exit: {e}\")\n # Flush any buffered EOF-tail data BEFORE the WS Close: sending Close\n # first would let the daemon discard the tail.\n if clean_eof:\n tail = pending_in.strip()\n if tail and not done.is_set():\n try:\n locked_send(sock, send_lock, tail, timeout=_cleanup_timeout())\n except Exception as e:\n log(f\"eof flush failed: {e}\")\n try:\n locked_send(sock, send_lock, b\"\", opcode=0x8, timeout=_cleanup_timeout())\n except Exception as e:\n log(f\"close send failed: {e}\")\n try:\n try:\n sock.shutdown(socket.SHUT_RDWR)\n except OSError:\n pass\n sock.close()\n except OSError:\n pass\n # Bound termination: wake the reader so no thread is left hung on recv.\n t.join(timeout=0.5)\n # A reader still exiting could commit output after this check. In that\n # case pristine stdio cannot be proven, so never permit fallback.\n if t.is_alive():\n output_forwarded.set()\n if clean_eof:\n log(\"exit\")\n return EXIT_SERVED\n # Reader died, first RPC timed out, post-upgrade I/O timed out, or stdin\n # broke: fail open only while stdio is still pristine -- no stdin byte\n # ever consumed AND no daemon payload ever committed to stdout. Committed\n # output forces a mid-session exit so Desktop reconnects instead of\n # falling back to stock Codex on a corrupted stream.\n rc = EXIT_MID_SESSION if (stdin_bytes or output_forwarded.is_set()) else EXIT_PRE_STDIO\n log(f\"exit rc={rc}\")\n return rc\n\nif __name__ == \"__main__\":\n raise SystemExit(main())\n"; + const BRIDGE_SOURCE = "#!/usr/bin/env python3\n\"\"\"Stdio JSONL (Desktop) <-> WebSocket-over-unix (managed app-server daemon).\nReversible: remove CODEX_CLI_PATH wrapper. Does not read Desktop private pipes.\n\"\"\"\nfrom __future__ import annotations\nimport base64, hashlib, json, os, select, socket, struct, sys, threading, time\n\nSOCK = os.environ.get(\n \"CODEX_APP_SERVER_SOCK\",\n os.path.expanduser(\"~/.codex/app-server-control/app-server-control.sock\"),\n)\nLOG = os.environ.get(\"CODEX_STDIO_BRIDGE_LOG\", os.path.expanduser(\"~/.codex/bin/codex-stdio-to-daemon-ws.log\"))\nWS_GUID = \"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\"\n\n# Wrapper contract: 0 served the session, 1 failed while stdio was still\n# pristine -- no stdin byte consumed and no stdout frame committed (wrapper\n# falls back to real Codex), 2 failed mid-session (wrapper exits promptly so\n# Desktop reconnects; the fallback must never run on half-consumed stdin or\n# dirty stdout).\nEXIT_SERVED = 0\nEXIT_PRE_STDIO = 1\nEXIT_MID_SESSION = 2\n\nCONNECT_TIMEOUT_S = float(os.environ.get(\"CODEX_BRIDGE_CONNECT_TIMEOUT\", \"10\"))\n# Bound from connect to the matching first daemon response: a daemon that\n# completes the upgrade but never answers the first RPC must not hold Desktop\n# past this. Only the matching initialize response/error (or the deadline\n# itself) clears it; notifications never do.\nFIRST_MESSAGE_TIMEOUT_S = float(os.environ.get(\"CODEX_BRIDGE_FIRST_MESSAGE_TIMEOUT\", \"30\"))\n# Bound on every mid-session socket send and stdout write: a wedged or\n# non-reading peer must not hang Desktop past this. Reads after a successful\n# init block with no timeout so healthy idle sessions survive silence.\nIO_TIMEOUT_S = float(os.environ.get(\"CODEX_BRIDGE_IO_TIMEOUT\", \"30\"))\n\ndef log(msg: str) -> None:\n try:\n with open(LOG, \"a\") as f:\n f.write(time.strftime(\"%Y-%m-%dT%H:%M:%SZ\", time.gmtime()) + \" \" + msg + \"\\n\")\n except OSError:\n pass\n\ndef ws_connect(path: str, timeout: float = CONNECT_TIMEOUT_S):\n \"\"\"Connect + HTTP upgrade under one absolute monotonic deadline.\n\n Returns (socket, trailing) where trailing holds bytes coalesced after the\n upgrade headers (TCP may deliver the 101 and the first WS frame together;\n discarding them would lose the first message). Requires HTTP/1.1 101 plus\n a matching Sec-WebSocket-Accept so a 200 (or any non-101) fails fast even\n when the accept hash happens to be valid.\n \"\"\"\n deadline = time.monotonic() + timeout\n s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)\n s.settimeout(timeout)\n try:\n s.connect(path)\n key = base64.b64encode(os.urandom(16)).decode()\n expected = base64.b64encode(hashlib.sha1((key + WS_GUID).encode()).digest()).decode()\n req = (\n f\"GET /rpc HTTP/1.1\\r\\nHost: localhost\\r\\nUpgrade: websocket\\r\\n\"\n f\"Connection: Upgrade\\r\\nSec-WebSocket-Key: {key}\\r\\nSec-WebSocket-Version: 13\\r\\n\\r\\n\"\n ).encode()\n s.settimeout(max(0.0, deadline - time.monotonic()))\n s.sendall(req)\n buf = b\"\"\n while True:\n if b\"\\r\\n\\r\\n\" in buf:\n break\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"WebSocket upgrade timed out\")\n if len(buf) > 65536:\n raise ConnectionError(\"WS upgrade headers too large\")\n s.settimeout(remaining)\n chunk = s.recv(4096)\n if not chunk:\n raise ConnectionError(\"daemon closed during WS upgrade\")\n buf += chunk\n head, _, trailing = buf.partition(b\"\\r\\n\\r\\n\")\n lines = head.split(b\"\\r\\n\")\n if not lines or lines[0].split(b\" \", 2)[:2] != [b\"HTTP/1.1\", b\"101\"]:\n raise ConnectionError(f\"WS upgrade failed: {head[:200]!r}\")\n accept = None\n for line in lines[1:]:\n name, sep, value = line.partition(b\":\")\n if sep and name.strip().lower() == b\"sec-websocket-accept\":\n accept = value.strip().decode(\"latin-1\")\n if accept != expected:\n raise ConnectionError(\"WS upgrade accept mismatch\")\n except Exception:\n try:\n s.close()\n except OSError:\n pass\n raise\n # Keep one socket mode across reader and writer threads. Reads wait\n # indefinitely in select; sends use their own absolute deadline.\n s.setblocking(False)\n return s, trailing\n\ndef mask_send(sock: socket.socket, payload: bytes, opcode: int = 1, deadline=None) -> None:\n mask = os.urandom(4)\n ln = len(payload)\n if ln < 126:\n hdr = bytes([0x80 | opcode, 0x80 | ln])\n elif ln < 65536:\n hdr = bytes([0x80 | opcode, 0x80 | 126]) + struct.pack(\"!H\", ln)\n else:\n hdr = bytes([0x80 | opcode, 0x80 | 127]) + struct.pack(\"!Q\", ln)\n view = memoryview(hdr + mask + bytes(b ^ mask[i % 4] for i, b in enumerate(payload)))\n while view:\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"socket write timed out\")\n _, writable, _ = select.select([], [sock], [], remaining)\n if not writable:\n raise TimeoutError(\"socket write timed out\")\n try:\n n = sock.send(view)\n except BlockingIOError:\n continue\n if not n:\n raise ConnectionError(\"socket closed during write\")\n view = view[n:]\n\ndef locked_send(sock: socket.socket, lock: threading.Lock, payload: bytes, opcode: int = 1, timeout: float = CONNECT_TIMEOUT_S) -> None:\n \"\"\"Serialized send sharing one monotonic budget across lock wait + send.\n\n A non-reading peer must never wedge pong writes or timeout cleanup: on\n expiry raise TimeoutError so the caller closes and exits without hanging.\n \"\"\"\n if timeout is None:\n timeout = CONNECT_TIMEOUT_S\n deadline = time.monotonic() + timeout\n if timeout <= 0:\n raise TimeoutError(\"send budget expired\")\n if not lock.acquire(timeout=timeout):\n raise TimeoutError(\"send lock timed out\")\n try:\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"send budget expired\")\n mask_send(sock, payload, opcode=opcode, deadline=deadline)\n finally:\n lock.release()\n\ndef _buffered_reader(sock: socket.socket, initial: bytes = b\"\"):\n buf = bytearray(initial)\n def recv(n):\n while len(buf) < n:\n select.select([sock], [], [])\n try:\n chunk = sock.recv(n - len(buf))\n except BlockingIOError:\n continue\n if not chunk:\n raise ConnectionError(\"eof\")\n buf.extend(chunk)\n out = bytes(buf[:n])\n del buf[:n]\n return out\n return recv\n\ndef read_frame(recv):\n hdr = recv(2)\n fin = bool(hdr[0] & 0x80)\n opcode = hdr[0] & 0x0F\n masked = bool(hdr[1] & 0x80)\n ln = hdr[1] & 0x7F\n if ln == 126:\n ln = struct.unpack(\"!H\", recv(2))[0]\n elif ln == 127:\n ln = struct.unpack(\"!Q\", recv(8))[0]\n mask = recv(4) if masked else b\"\"\n payload = recv(ln)\n if masked:\n payload = bytes(b ^ mask[i % 4] for i, b in enumerate(payload))\n return fin, opcode, payload\n\ndef read_message(recv, send_pong):\n \"\"\"Next complete data message, assembling continuation frames.\n\n Control frames are handled inline (pong answered, close reported) so a\n fragmented message split around a ping still reassembles.\n \"\"\"\n opcode = None\n parts = []\n while True:\n fin, op, payload = read_frame(recv)\n if op == 0x8:\n return (\"close\", None, b\"\")\n if op == 0x9:\n send_pong(payload)\n continue\n if op == 0xA:\n continue\n if op in (0x1, 0x2):\n if opcode is not None:\n raise ConnectionError(\"data frame inside fragmented message\")\n if fin:\n return (\"data\", op, payload)\n opcode = op\n parts.append(payload)\n continue\n if op == 0x0:\n if opcode is None:\n raise ConnectionError(\"stray continuation frame\")\n parts.append(payload)\n if fin:\n return (\"data\", opcode, b\"\".join(parts))\n continue\n raise ConnectionError(f\"unsupported opcode {op}\")\n\ndef _is_daemon_response(payload: bytes, expect_id=None, known_ids=None) -> bool:\n \"\"\"True only for the matching JSON-RPC response/error, never a notification.\n\n The first-RPC timer must survive unrelated daemon notifications and foreign\n responses: only the response/error whose id matches the client's initialize\n (or, before it is known, any id the client already sent) proves the first\n RPC got an answer. Timeouts clear it via the deadline, never via data.\n \"\"\"\n try:\n text = payload.decode(\"utf-8\").strip()\n msg = json.loads(text)\n except Exception:\n return False\n if not isinstance(msg, dict):\n return False\n rid = msg.get(\"id\")\n if rid is None or \"method\" in msg or (\"result\" not in msg and \"error\" not in msg):\n return False\n if expect_id is not None:\n return rid == expect_id\n if known_ids:\n return rid in known_ids\n return False\n\ndef _bounded_stdout_write(data: bytes, timeout: float = IO_TIMEOUT_S) -> None:\n \"\"\"Write one daemon frame to stdout without hanging on a non-reading peer.\n\n Waits for writability under the mid-session write budget and writes via\n os.write in a loop, so a Desktop that stopped reading cannot hang the\n bridge and then exit 1 on dirty stdout.\n \"\"\"\n if timeout is None:\n timeout = IO_TIMEOUT_S\n if timeout <= 0:\n raise TimeoutError(\"stdout budget expired\")\n fd = sys.stdout.fileno()\n deadline = time.monotonic() + timeout\n was_blocking = os.get_blocking(fd)\n os.set_blocking(fd, False)\n try:\n view = memoryview(data)\n while view:\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"stdout write timed out\")\n _, writable, _ = select.select([], [fd], [], remaining)\n if not writable:\n raise TimeoutError(\"stdout write timed out\")\n try:\n n = os.write(fd, view)\n except BlockingIOError:\n continue\n view = view[n:]\n finally:\n os.set_blocking(fd, was_blocking)\n\ndef stdout_writer(sock: socket.socket, recv, send_pong, done: threading.Event, first_msg: threading.Event, output_forwarded: threading.Event, client_ids=None, init_state=None) -> None:\n try:\n while True:\n kind, opcode, payload = read_message(recv, send_pong)\n if kind == \"close\":\n log(\"daemon WS close\")\n return\n if opcode == 0x1:\n # Desktop StdioConnection expects newline-delimited JSON text\n if not payload.endswith(b\"\\n\"):\n payload += b\"\\n\"\n # Commit BEFORE writing: once a daemon byte is about to hit\n # stdout, the session is dirty and the fallback must never run,\n # even if the write itself blocks or fails.\n output_forwarded.set()\n _bounded_stdout_write(payload)\n if opcode == 0x1:\n expect = (init_state or {}).get(\"initialize_id\") if isinstance(init_state, dict) else None\n if _is_daemon_response(payload, expect, client_ids or ()):\n first_msg.set()\n except Exception as e:\n log(f\"stdout_writer exit: {e}\")\n finally:\n done.set()\n\ndef main() -> int:\n log(f\"start sock={SOCK}\")\n try:\n sock, pending = ws_connect(SOCK)\n except Exception as e:\n log(f\"connect failed: {e}\")\n sys.stderr.write(f\"codex-stdio-to-daemon-ws: {e}\\n\")\n return EXIT_PRE_STDIO\n log(\"connected\")\n connected_at = time.monotonic()\n first_deadline = connected_at + FIRST_MESSAGE_TIMEOUT_S\n send_lock = threading.Lock()\n done = threading.Event()\n first_msg = threading.Event()\n output_forwarded = threading.Event()\n client_ids = set()\n init_state = {\"initialize_id\": None}\n\n def _send_timeout() -> float:\n # Before the first matching daemon response the remaining first-RPC\n # budget bounds every send; after it each send gets the mid-session\n # write budget. Idle reads are never timed out.\n if not first_msg.is_set():\n return max(0.0, first_deadline - time.monotonic())\n return IO_TIMEOUT_S\n\n def _cleanup_timeout() -> float:\n # Timeout cleanup stays within the expired session budget: once the\n # first-RPC deadline has passed, close directly instead of opening\n # fresh windows that let a short budget take several timeout periods.\n if not first_msg.is_set():\n return max(0.0, first_deadline - time.monotonic())\n return CONNECT_TIMEOUT_S\n\n def send_pong(payload: bytes) -> None:\n locked_send(sock, send_lock, payload, opcode=0xA, timeout=_send_timeout())\n recv = _buffered_reader(sock, pending)\n t = threading.Thread(target=stdout_writer, args=(sock, recv, send_pong, done, first_msg, output_forwarded, client_ids, init_state), daemon=True)\n t.start()\n # Byte-precise commit point: the fallback may run only while zero stdin\n # bytes have left the pipe and no daemon payload was committed to stdout.\n # Reads use os.read into a userspace line buffer (buffered readline could\n # readahead past the commit point), and every byte read counts -- even a\n # blank line. first_msg tracks the matching daemon response/error, never\n # a notification.\n stdin_bytes = 0\n pending_in = b\"\"\n clean_eof = False\n try:\n fd = sys.stdin.fileno()\n while not done.is_set():\n if not first_msg.is_set() and time.monotonic() >= first_deadline:\n log(\"first daemon message timed out\")\n break\n ready, _, _ = select.select([fd], [], [], 0.5)\n if not ready:\n continue\n chunk = os.read(fd, 65536)\n if not chunk:\n clean_eof = True\n break\n stdin_bytes += len(chunk)\n pending_in += chunk\n while b\"\\n\" in pending_in:\n line, _, pending_in = pending_in.partition(b\"\\n\")\n line = line.strip()\n if not line:\n continue\n try:\n try:\n obj = json.loads(line.decode(\"utf-8\"))\n except Exception:\n obj = None\n if isinstance(obj, dict) and obj.get(\"id\") is not None:\n client_ids.add(obj.get(\"id\"))\n if obj.get(\"method\") == \"initialize\" and init_state[\"initialize_id\"] is None:\n init_state[\"initialize_id\"] = obj.get(\"id\")\n except Exception:\n pass\n try:\n locked_send(sock, send_lock, line, timeout=_send_timeout())\n except TimeoutError as e:\n log(f\"stdin forward timed out: {e}\")\n break\n except Exception as e:\n log(f\"stdin forward failed: {e}\")\n break\n else:\n continue\n break\n except Exception as e:\n log(f\"stdin loop exit: {e}\")\n # Flush any buffered EOF-tail data BEFORE the WS Close: sending Close\n # first would let the daemon discard the tail.\n if clean_eof:\n tail = pending_in.strip()\n if tail and not done.is_set():\n try:\n locked_send(sock, send_lock, tail, timeout=_cleanup_timeout())\n except Exception as e:\n log(f\"eof flush failed: {e}\")\n try:\n locked_send(sock, send_lock, b\"\", opcode=0x8, timeout=_cleanup_timeout())\n except Exception as e:\n log(f\"close send failed: {e}\")\n try:\n try:\n sock.shutdown(socket.SHUT_RDWR)\n except OSError:\n pass\n sock.close()\n except OSError:\n pass\n # Bound termination: wake the reader so no thread is left hung on recv.\n t.join(timeout=0.5)\n # A reader still exiting could commit output after this check. In that\n # case pristine stdio cannot be proven, so never permit fallback.\n if t.is_alive():\n output_forwarded.set()\n if clean_eof and (stdin_bytes or output_forwarded.is_set()):\n log(\"exit\")\n return EXIT_SERVED\n # Reader died, first RPC timed out, post-upgrade I/O timed out, or stdin\n # broke: fail open only while stdio is still pristine -- no stdin byte\n # ever consumed AND no daemon payload ever committed to stdout. Committed\n # output forces a mid-session exit so Desktop reconnects instead of\n # falling back to stock Codex on a corrupted stream.\n rc = EXIT_MID_SESSION if (stdin_bytes or output_forwarded.is_set()) else EXIT_PRE_STDIO\n log(f\"exit rc={rc}\")\n return rc\n\nif __name__ == \"__main__\":\n raise SystemExit(main())\n"; __webpack_require__.d(__webpack_exports__, {}, { w: BRIDGE_SOURCE }); @@ -28825,19 +28825,30 @@ var __webpack_modules__ = { let daemon = false; let proxy = false; let generate = false; + let oneShot = false; for (const arg of argv ?? []){ + if ([ + "--print-startup-config", + "--startup-sign-out", + "help", + "--help", + "-h", + "--version", + "-V" + ].includes(arg)) oneShot = true; if (arg === "app-server") appServer = true; else if (arg === "daemon") daemon = true; else if (arg === "proxy") proxy = true; else if (arg === "generate-ts" || arg === "generate-json-schema") generate = true; } - return appServer && !daemon && !proxy && !generate; + return appServer && !daemon && !proxy && !generate && !oneShot; } function renderWrapperScript({ realPath, bridgePath, wrapperLogPath, codexHome, bridgeLogPath }) { return `#!/bin/bash # Installed by \`gbot codex desktop-shim install\`. Rewrites ChatGPT Desktop's # \`codex ... app-server\` spawn into a stdio<->WebSocket bridge onto the managed # daemon's control socket. Reversible: \`gbot codex desktop-shim uninstall\`. +# Startup configuration, sign-out, help and version requests use the real CLI. # The daemon is kept up by the LaunchAgent login script, not here: this hot path # never starts the daemon itself, so a wedged daemon lock cannot hang # Desktop. Fail-open runs ONLY while stdio is still pristine (no stdin @@ -28870,9 +28881,10 @@ if [[ ! -x "$REAL" ]]; then fi fi -has_app_server=0; has_daemon=0; has_proxy=0; has_generate=0 +has_app_server=0; has_daemon=0; has_proxy=0; has_generate=0; has_one_shot=0 for a in "$@"; do case "$a" in + --print-startup-config|--startup-sign-out|help|--help|-h|--version|-V) has_one_shot=1 ;; app-server) has_app_server=1 ;; daemon) has_daemon=1 ;; proxy) has_proxy=1 ;; @@ -28886,7 +28898,7 @@ preflight() { python3 -c 'import os,socket,sys; s=socket.socket(socket.AF_UNIX,socket.SOCK_STREAM); s.settimeout(float(os.environ.get("CODEX_DESKTOP_PREFLIGHT_TIMEOUT","3"))); s.connect(sys.argv[1]); s.close()' "$SOCK" 2>/dev/null } -if [[ "$has_app_server" -eq 1 && "$has_daemon" -eq 0 && "$has_proxy" -eq 0 && "$has_generate" -eq 0 ]]; then +if [[ "$has_app_server" -eq 1 && "$has_daemon" -eq 0 && "$has_proxy" -eq 0 && "$has_generate" -eq 0 && "$has_one_shot" -eq 0 ]]; then if [[ -f "$BRIDGE" ]] && command -v python3 >/dev/null 2>&1; then if preflight; then echo "$(ts) rewrite -> stdio-ws bridge" >>"$LOG" 2>/dev/null || true diff --git a/artifact/bin/gbot.mjs b/artifact/bin/gbot.mjs index 959a57e..f9ead77 100755 --- a/artifact/bin/gbot.mjs +++ b/artifact/bin/gbot.mjs @@ -47205,7 +47205,7 @@ var __webpack_modules__ = { }); }, "./src/core/desktop-shim-bridge.js" (__unused_rspack___webpack_module__, __webpack_exports__, __webpack_require__) { - const BRIDGE_SOURCE = "#!/usr/bin/env python3\n\"\"\"Stdio JSONL (Desktop) <-> WebSocket-over-unix (managed app-server daemon).\nReversible: remove CODEX_CLI_PATH wrapper. Does not read Desktop private pipes.\n\"\"\"\nfrom __future__ import annotations\nimport base64, hashlib, json, os, select, socket, struct, sys, threading, time\n\nSOCK = os.environ.get(\n \"CODEX_APP_SERVER_SOCK\",\n os.path.expanduser(\"~/.codex/app-server-control/app-server-control.sock\"),\n)\nLOG = os.environ.get(\"CODEX_STDIO_BRIDGE_LOG\", os.path.expanduser(\"~/.codex/bin/codex-stdio-to-daemon-ws.log\"))\nWS_GUID = \"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\"\n\n# Wrapper contract: 0 served the session, 1 failed while stdio was still\n# pristine -- no stdin byte consumed and no stdout frame committed (wrapper\n# falls back to real Codex), 2 failed mid-session (wrapper exits promptly so\n# Desktop reconnects; the fallback must never run on half-consumed stdin or\n# dirty stdout).\nEXIT_SERVED = 0\nEXIT_PRE_STDIO = 1\nEXIT_MID_SESSION = 2\n\nCONNECT_TIMEOUT_S = float(os.environ.get(\"CODEX_BRIDGE_CONNECT_TIMEOUT\", \"10\"))\n# Bound from connect to the matching first daemon response: a daemon that\n# completes the upgrade but never answers the first RPC must not hold Desktop\n# past this. Only the matching initialize response/error (or the deadline\n# itself) clears it; notifications never do.\nFIRST_MESSAGE_TIMEOUT_S = float(os.environ.get(\"CODEX_BRIDGE_FIRST_MESSAGE_TIMEOUT\", \"30\"))\n# Bound on every mid-session socket send and stdout write: a wedged or\n# non-reading peer must not hang Desktop past this. Reads after a successful\n# init block with no timeout so healthy idle sessions survive silence.\nIO_TIMEOUT_S = float(os.environ.get(\"CODEX_BRIDGE_IO_TIMEOUT\", \"30\"))\n\ndef log(msg: str) -> None:\n try:\n with open(LOG, \"a\") as f:\n f.write(time.strftime(\"%Y-%m-%dT%H:%M:%SZ\", time.gmtime()) + \" \" + msg + \"\\n\")\n except OSError:\n pass\n\ndef ws_connect(path: str, timeout: float = CONNECT_TIMEOUT_S):\n \"\"\"Connect + HTTP upgrade under one absolute monotonic deadline.\n\n Returns (socket, trailing) where trailing holds bytes coalesced after the\n upgrade headers (TCP may deliver the 101 and the first WS frame together;\n discarding them would lose the first message). Requires HTTP/1.1 101 plus\n a matching Sec-WebSocket-Accept so a 200 (or any non-101) fails fast even\n when the accept hash happens to be valid.\n \"\"\"\n deadline = time.monotonic() + timeout\n s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)\n s.settimeout(timeout)\n try:\n s.connect(path)\n key = base64.b64encode(os.urandom(16)).decode()\n expected = base64.b64encode(hashlib.sha1((key + WS_GUID).encode()).digest()).decode()\n req = (\n f\"GET /rpc HTTP/1.1\\r\\nHost: localhost\\r\\nUpgrade: websocket\\r\\n\"\n f\"Connection: Upgrade\\r\\nSec-WebSocket-Key: {key}\\r\\nSec-WebSocket-Version: 13\\r\\n\\r\\n\"\n ).encode()\n s.settimeout(max(0.0, deadline - time.monotonic()))\n s.sendall(req)\n buf = b\"\"\n while True:\n if b\"\\r\\n\\r\\n\" in buf:\n break\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"WebSocket upgrade timed out\")\n if len(buf) > 65536:\n raise ConnectionError(\"WS upgrade headers too large\")\n s.settimeout(remaining)\n chunk = s.recv(4096)\n if not chunk:\n raise ConnectionError(\"daemon closed during WS upgrade\")\n buf += chunk\n head, _, trailing = buf.partition(b\"\\r\\n\\r\\n\")\n lines = head.split(b\"\\r\\n\")\n if not lines or lines[0].split(b\" \", 2)[:2] != [b\"HTTP/1.1\", b\"101\"]:\n raise ConnectionError(f\"WS upgrade failed: {head[:200]!r}\")\n accept = None\n for line in lines[1:]:\n name, sep, value = line.partition(b\":\")\n if sep and name.strip().lower() == b\"sec-websocket-accept\":\n accept = value.strip().decode(\"latin-1\")\n if accept != expected:\n raise ConnectionError(\"WS upgrade accept mismatch\")\n except Exception:\n try:\n s.close()\n except OSError:\n pass\n raise\n # Keep one socket mode across reader and writer threads. Reads wait\n # indefinitely in select; sends use their own absolute deadline.\n s.setblocking(False)\n return s, trailing\n\ndef mask_send(sock: socket.socket, payload: bytes, opcode: int = 1, deadline=None) -> None:\n mask = os.urandom(4)\n ln = len(payload)\n if ln < 126:\n hdr = bytes([0x80 | opcode, 0x80 | ln])\n elif ln < 65536:\n hdr = bytes([0x80 | opcode, 0x80 | 126]) + struct.pack(\"!H\", ln)\n else:\n hdr = bytes([0x80 | opcode, 0x80 | 127]) + struct.pack(\"!Q\", ln)\n view = memoryview(hdr + mask + bytes(b ^ mask[i % 4] for i, b in enumerate(payload)))\n while view:\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"socket write timed out\")\n _, writable, _ = select.select([], [sock], [], remaining)\n if not writable:\n raise TimeoutError(\"socket write timed out\")\n try:\n n = sock.send(view)\n except BlockingIOError:\n continue\n if not n:\n raise ConnectionError(\"socket closed during write\")\n view = view[n:]\n\ndef locked_send(sock: socket.socket, lock: threading.Lock, payload: bytes, opcode: int = 1, timeout: float = CONNECT_TIMEOUT_S) -> None:\n \"\"\"Serialized send sharing one monotonic budget across lock wait + send.\n\n A non-reading peer must never wedge pong writes or timeout cleanup: on\n expiry raise TimeoutError so the caller closes and exits without hanging.\n \"\"\"\n if timeout is None:\n timeout = CONNECT_TIMEOUT_S\n deadline = time.monotonic() + timeout\n if timeout <= 0:\n raise TimeoutError(\"send budget expired\")\n if not lock.acquire(timeout=timeout):\n raise TimeoutError(\"send lock timed out\")\n try:\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"send budget expired\")\n mask_send(sock, payload, opcode=opcode, deadline=deadline)\n finally:\n lock.release()\n\ndef _buffered_reader(sock: socket.socket, initial: bytes = b\"\"):\n buf = bytearray(initial)\n def recv(n):\n while len(buf) < n:\n select.select([sock], [], [])\n try:\n chunk = sock.recv(n - len(buf))\n except BlockingIOError:\n continue\n if not chunk:\n raise ConnectionError(\"eof\")\n buf.extend(chunk)\n out = bytes(buf[:n])\n del buf[:n]\n return out\n return recv\n\ndef read_frame(recv):\n hdr = recv(2)\n fin = bool(hdr[0] & 0x80)\n opcode = hdr[0] & 0x0F\n masked = bool(hdr[1] & 0x80)\n ln = hdr[1] & 0x7F\n if ln == 126:\n ln = struct.unpack(\"!H\", recv(2))[0]\n elif ln == 127:\n ln = struct.unpack(\"!Q\", recv(8))[0]\n mask = recv(4) if masked else b\"\"\n payload = recv(ln)\n if masked:\n payload = bytes(b ^ mask[i % 4] for i, b in enumerate(payload))\n return fin, opcode, payload\n\ndef read_message(recv, send_pong):\n \"\"\"Next complete data message, assembling continuation frames.\n\n Control frames are handled inline (pong answered, close reported) so a\n fragmented message split around a ping still reassembles.\n \"\"\"\n opcode = None\n parts = []\n while True:\n fin, op, payload = read_frame(recv)\n if op == 0x8:\n return (\"close\", None, b\"\")\n if op == 0x9:\n send_pong(payload)\n continue\n if op == 0xA:\n continue\n if op in (0x1, 0x2):\n if opcode is not None:\n raise ConnectionError(\"data frame inside fragmented message\")\n if fin:\n return (\"data\", op, payload)\n opcode = op\n parts.append(payload)\n continue\n if op == 0x0:\n if opcode is None:\n raise ConnectionError(\"stray continuation frame\")\n parts.append(payload)\n if fin:\n return (\"data\", opcode, b\"\".join(parts))\n continue\n raise ConnectionError(f\"unsupported opcode {op}\")\n\ndef _is_daemon_response(payload: bytes, expect_id=None, known_ids=None) -> bool:\n \"\"\"True only for the matching JSON-RPC response/error, never a notification.\n\n The first-RPC timer must survive unrelated daemon notifications and foreign\n responses: only the response/error whose id matches the client's initialize\n (or, before it is known, any id the client already sent) proves the first\n RPC got an answer. Timeouts clear it via the deadline, never via data.\n \"\"\"\n try:\n text = payload.decode(\"utf-8\").strip()\n msg = json.loads(text)\n except Exception:\n return False\n if not isinstance(msg, dict):\n return False\n rid = msg.get(\"id\")\n if rid is None or \"method\" in msg or (\"result\" not in msg and \"error\" not in msg):\n return False\n if expect_id is not None:\n return rid == expect_id\n if known_ids:\n return rid in known_ids\n return False\n\ndef _bounded_stdout_write(data: bytes, timeout: float = IO_TIMEOUT_S) -> None:\n \"\"\"Write one daemon frame to stdout without hanging on a non-reading peer.\n\n Waits for writability under the mid-session write budget and writes via\n os.write in a loop, so a Desktop that stopped reading cannot hang the\n bridge and then exit 1 on dirty stdout.\n \"\"\"\n if timeout is None:\n timeout = IO_TIMEOUT_S\n if timeout <= 0:\n raise TimeoutError(\"stdout budget expired\")\n fd = sys.stdout.fileno()\n deadline = time.monotonic() + timeout\n was_blocking = os.get_blocking(fd)\n os.set_blocking(fd, False)\n try:\n view = memoryview(data)\n while view:\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"stdout write timed out\")\n _, writable, _ = select.select([], [fd], [], remaining)\n if not writable:\n raise TimeoutError(\"stdout write timed out\")\n try:\n n = os.write(fd, view)\n except BlockingIOError:\n continue\n view = view[n:]\n finally:\n os.set_blocking(fd, was_blocking)\n\ndef stdout_writer(sock: socket.socket, recv, send_pong, done: threading.Event, first_msg: threading.Event, output_forwarded: threading.Event, client_ids=None, init_state=None) -> None:\n try:\n while True:\n kind, opcode, payload = read_message(recv, send_pong)\n if kind == \"close\":\n log(\"daemon WS close\")\n return\n if opcode == 0x1:\n # Desktop StdioConnection expects newline-delimited JSON text\n if not payload.endswith(b\"\\n\"):\n payload += b\"\\n\"\n # Commit BEFORE writing: once a daemon byte is about to hit\n # stdout, the session is dirty and the fallback must never run,\n # even if the write itself blocks or fails.\n output_forwarded.set()\n _bounded_stdout_write(payload)\n if opcode == 0x1:\n expect = (init_state or {}).get(\"initialize_id\") if isinstance(init_state, dict) else None\n if _is_daemon_response(payload, expect, client_ids or ()):\n first_msg.set()\n except Exception as e:\n log(f\"stdout_writer exit: {e}\")\n finally:\n done.set()\n\ndef main() -> int:\n log(f\"start sock={SOCK}\")\n try:\n sock, pending = ws_connect(SOCK)\n except Exception as e:\n log(f\"connect failed: {e}\")\n sys.stderr.write(f\"codex-stdio-to-daemon-ws: {e}\\n\")\n return EXIT_PRE_STDIO\n log(\"connected\")\n connected_at = time.monotonic()\n first_deadline = connected_at + FIRST_MESSAGE_TIMEOUT_S\n send_lock = threading.Lock()\n done = threading.Event()\n first_msg = threading.Event()\n output_forwarded = threading.Event()\n client_ids = set()\n init_state = {\"initialize_id\": None}\n\n def _send_timeout() -> float:\n # Before the first matching daemon response the remaining first-RPC\n # budget bounds every send; after it each send gets the mid-session\n # write budget. Idle reads are never timed out.\n if not first_msg.is_set():\n return max(0.0, first_deadline - time.monotonic())\n return IO_TIMEOUT_S\n\n def _cleanup_timeout() -> float:\n # Timeout cleanup stays within the expired session budget: once the\n # first-RPC deadline has passed, close directly instead of opening\n # fresh windows that let a short budget take several timeout periods.\n if not first_msg.is_set():\n return max(0.0, first_deadline - time.monotonic())\n return CONNECT_TIMEOUT_S\n\n def send_pong(payload: bytes) -> None:\n locked_send(sock, send_lock, payload, opcode=0xA, timeout=_send_timeout())\n recv = _buffered_reader(sock, pending)\n t = threading.Thread(target=stdout_writer, args=(sock, recv, send_pong, done, first_msg, output_forwarded, client_ids, init_state), daemon=True)\n t.start()\n # Byte-precise commit point: the fallback may run only while zero stdin\n # bytes have left the pipe and no daemon payload was committed to stdout.\n # Reads use os.read into a userspace line buffer (buffered readline could\n # readahead past the commit point), and every byte read counts -- even a\n # blank line. first_msg tracks the matching daemon response/error, never\n # a notification.\n stdin_bytes = 0\n pending_in = b\"\"\n clean_eof = False\n try:\n fd = sys.stdin.fileno()\n while not done.is_set():\n if not first_msg.is_set() and time.monotonic() >= first_deadline:\n log(\"first daemon message timed out\")\n break\n ready, _, _ = select.select([fd], [], [], 0.5)\n if not ready:\n continue\n chunk = os.read(fd, 65536)\n if not chunk:\n clean_eof = True\n break\n stdin_bytes += len(chunk)\n pending_in += chunk\n while b\"\\n\" in pending_in:\n line, _, pending_in = pending_in.partition(b\"\\n\")\n line = line.strip()\n if not line:\n continue\n try:\n try:\n obj = json.loads(line.decode(\"utf-8\"))\n except Exception:\n obj = None\n if isinstance(obj, dict) and obj.get(\"id\") is not None:\n client_ids.add(obj.get(\"id\"))\n if obj.get(\"method\") == \"initialize\" and init_state[\"initialize_id\"] is None:\n init_state[\"initialize_id\"] = obj.get(\"id\")\n except Exception:\n pass\n try:\n locked_send(sock, send_lock, line, timeout=_send_timeout())\n except TimeoutError as e:\n log(f\"stdin forward timed out: {e}\")\n break\n except Exception as e:\n log(f\"stdin forward failed: {e}\")\n break\n else:\n continue\n break\n except Exception as e:\n log(f\"stdin loop exit: {e}\")\n # Flush any buffered EOF-tail data BEFORE the WS Close: sending Close\n # first would let the daemon discard the tail.\n if clean_eof:\n tail = pending_in.strip()\n if tail and not done.is_set():\n try:\n locked_send(sock, send_lock, tail, timeout=_cleanup_timeout())\n except Exception as e:\n log(f\"eof flush failed: {e}\")\n try:\n locked_send(sock, send_lock, b\"\", opcode=0x8, timeout=_cleanup_timeout())\n except Exception as e:\n log(f\"close send failed: {e}\")\n try:\n try:\n sock.shutdown(socket.SHUT_RDWR)\n except OSError:\n pass\n sock.close()\n except OSError:\n pass\n # Bound termination: wake the reader so no thread is left hung on recv.\n t.join(timeout=0.5)\n # A reader still exiting could commit output after this check. In that\n # case pristine stdio cannot be proven, so never permit fallback.\n if t.is_alive():\n output_forwarded.set()\n if clean_eof:\n log(\"exit\")\n return EXIT_SERVED\n # Reader died, first RPC timed out, post-upgrade I/O timed out, or stdin\n # broke: fail open only while stdio is still pristine -- no stdin byte\n # ever consumed AND no daemon payload ever committed to stdout. Committed\n # output forces a mid-session exit so Desktop reconnects instead of\n # falling back to stock Codex on a corrupted stream.\n rc = EXIT_MID_SESSION if (stdin_bytes or output_forwarded.is_set()) else EXIT_PRE_STDIO\n log(f\"exit rc={rc}\")\n return rc\n\nif __name__ == \"__main__\":\n raise SystemExit(main())\n"; + const BRIDGE_SOURCE = "#!/usr/bin/env python3\n\"\"\"Stdio JSONL (Desktop) <-> WebSocket-over-unix (managed app-server daemon).\nReversible: remove CODEX_CLI_PATH wrapper. Does not read Desktop private pipes.\n\"\"\"\nfrom __future__ import annotations\nimport base64, hashlib, json, os, select, socket, struct, sys, threading, time\n\nSOCK = os.environ.get(\n \"CODEX_APP_SERVER_SOCK\",\n os.path.expanduser(\"~/.codex/app-server-control/app-server-control.sock\"),\n)\nLOG = os.environ.get(\"CODEX_STDIO_BRIDGE_LOG\", os.path.expanduser(\"~/.codex/bin/codex-stdio-to-daemon-ws.log\"))\nWS_GUID = \"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\"\n\n# Wrapper contract: 0 served the session, 1 failed while stdio was still\n# pristine -- no stdin byte consumed and no stdout frame committed (wrapper\n# falls back to real Codex), 2 failed mid-session (wrapper exits promptly so\n# Desktop reconnects; the fallback must never run on half-consumed stdin or\n# dirty stdout).\nEXIT_SERVED = 0\nEXIT_PRE_STDIO = 1\nEXIT_MID_SESSION = 2\n\nCONNECT_TIMEOUT_S = float(os.environ.get(\"CODEX_BRIDGE_CONNECT_TIMEOUT\", \"10\"))\n# Bound from connect to the matching first daemon response: a daemon that\n# completes the upgrade but never answers the first RPC must not hold Desktop\n# past this. Only the matching initialize response/error (or the deadline\n# itself) clears it; notifications never do.\nFIRST_MESSAGE_TIMEOUT_S = float(os.environ.get(\"CODEX_BRIDGE_FIRST_MESSAGE_TIMEOUT\", \"30\"))\n# Bound on every mid-session socket send and stdout write: a wedged or\n# non-reading peer must not hang Desktop past this. Reads after a successful\n# init block with no timeout so healthy idle sessions survive silence.\nIO_TIMEOUT_S = float(os.environ.get(\"CODEX_BRIDGE_IO_TIMEOUT\", \"30\"))\n\ndef log(msg: str) -> None:\n try:\n with open(LOG, \"a\") as f:\n f.write(time.strftime(\"%Y-%m-%dT%H:%M:%SZ\", time.gmtime()) + \" \" + msg + \"\\n\")\n except OSError:\n pass\n\ndef ws_connect(path: str, timeout: float = CONNECT_TIMEOUT_S):\n \"\"\"Connect + HTTP upgrade under one absolute monotonic deadline.\n\n Returns (socket, trailing) where trailing holds bytes coalesced after the\n upgrade headers (TCP may deliver the 101 and the first WS frame together;\n discarding them would lose the first message). Requires HTTP/1.1 101 plus\n a matching Sec-WebSocket-Accept so a 200 (or any non-101) fails fast even\n when the accept hash happens to be valid.\n \"\"\"\n deadline = time.monotonic() + timeout\n s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)\n s.settimeout(timeout)\n try:\n s.connect(path)\n key = base64.b64encode(os.urandom(16)).decode()\n expected = base64.b64encode(hashlib.sha1((key + WS_GUID).encode()).digest()).decode()\n req = (\n f\"GET /rpc HTTP/1.1\\r\\nHost: localhost\\r\\nUpgrade: websocket\\r\\n\"\n f\"Connection: Upgrade\\r\\nSec-WebSocket-Key: {key}\\r\\nSec-WebSocket-Version: 13\\r\\n\\r\\n\"\n ).encode()\n s.settimeout(max(0.0, deadline - time.monotonic()))\n s.sendall(req)\n buf = b\"\"\n while True:\n if b\"\\r\\n\\r\\n\" in buf:\n break\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"WebSocket upgrade timed out\")\n if len(buf) > 65536:\n raise ConnectionError(\"WS upgrade headers too large\")\n s.settimeout(remaining)\n chunk = s.recv(4096)\n if not chunk:\n raise ConnectionError(\"daemon closed during WS upgrade\")\n buf += chunk\n head, _, trailing = buf.partition(b\"\\r\\n\\r\\n\")\n lines = head.split(b\"\\r\\n\")\n if not lines or lines[0].split(b\" \", 2)[:2] != [b\"HTTP/1.1\", b\"101\"]:\n raise ConnectionError(f\"WS upgrade failed: {head[:200]!r}\")\n accept = None\n for line in lines[1:]:\n name, sep, value = line.partition(b\":\")\n if sep and name.strip().lower() == b\"sec-websocket-accept\":\n accept = value.strip().decode(\"latin-1\")\n if accept != expected:\n raise ConnectionError(\"WS upgrade accept mismatch\")\n except Exception:\n try:\n s.close()\n except OSError:\n pass\n raise\n # Keep one socket mode across reader and writer threads. Reads wait\n # indefinitely in select; sends use their own absolute deadline.\n s.setblocking(False)\n return s, trailing\n\ndef mask_send(sock: socket.socket, payload: bytes, opcode: int = 1, deadline=None) -> None:\n mask = os.urandom(4)\n ln = len(payload)\n if ln < 126:\n hdr = bytes([0x80 | opcode, 0x80 | ln])\n elif ln < 65536:\n hdr = bytes([0x80 | opcode, 0x80 | 126]) + struct.pack(\"!H\", ln)\n else:\n hdr = bytes([0x80 | opcode, 0x80 | 127]) + struct.pack(\"!Q\", ln)\n view = memoryview(hdr + mask + bytes(b ^ mask[i % 4] for i, b in enumerate(payload)))\n while view:\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"socket write timed out\")\n _, writable, _ = select.select([], [sock], [], remaining)\n if not writable:\n raise TimeoutError(\"socket write timed out\")\n try:\n n = sock.send(view)\n except BlockingIOError:\n continue\n if not n:\n raise ConnectionError(\"socket closed during write\")\n view = view[n:]\n\ndef locked_send(sock: socket.socket, lock: threading.Lock, payload: bytes, opcode: int = 1, timeout: float = CONNECT_TIMEOUT_S) -> None:\n \"\"\"Serialized send sharing one monotonic budget across lock wait + send.\n\n A non-reading peer must never wedge pong writes or timeout cleanup: on\n expiry raise TimeoutError so the caller closes and exits without hanging.\n \"\"\"\n if timeout is None:\n timeout = CONNECT_TIMEOUT_S\n deadline = time.monotonic() + timeout\n if timeout <= 0:\n raise TimeoutError(\"send budget expired\")\n if not lock.acquire(timeout=timeout):\n raise TimeoutError(\"send lock timed out\")\n try:\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"send budget expired\")\n mask_send(sock, payload, opcode=opcode, deadline=deadline)\n finally:\n lock.release()\n\ndef _buffered_reader(sock: socket.socket, initial: bytes = b\"\"):\n buf = bytearray(initial)\n def recv(n):\n while len(buf) < n:\n select.select([sock], [], [])\n try:\n chunk = sock.recv(n - len(buf))\n except BlockingIOError:\n continue\n if not chunk:\n raise ConnectionError(\"eof\")\n buf.extend(chunk)\n out = bytes(buf[:n])\n del buf[:n]\n return out\n return recv\n\ndef read_frame(recv):\n hdr = recv(2)\n fin = bool(hdr[0] & 0x80)\n opcode = hdr[0] & 0x0F\n masked = bool(hdr[1] & 0x80)\n ln = hdr[1] & 0x7F\n if ln == 126:\n ln = struct.unpack(\"!H\", recv(2))[0]\n elif ln == 127:\n ln = struct.unpack(\"!Q\", recv(8))[0]\n mask = recv(4) if masked else b\"\"\n payload = recv(ln)\n if masked:\n payload = bytes(b ^ mask[i % 4] for i, b in enumerate(payload))\n return fin, opcode, payload\n\ndef read_message(recv, send_pong):\n \"\"\"Next complete data message, assembling continuation frames.\n\n Control frames are handled inline (pong answered, close reported) so a\n fragmented message split around a ping still reassembles.\n \"\"\"\n opcode = None\n parts = []\n while True:\n fin, op, payload = read_frame(recv)\n if op == 0x8:\n return (\"close\", None, b\"\")\n if op == 0x9:\n send_pong(payload)\n continue\n if op == 0xA:\n continue\n if op in (0x1, 0x2):\n if opcode is not None:\n raise ConnectionError(\"data frame inside fragmented message\")\n if fin:\n return (\"data\", op, payload)\n opcode = op\n parts.append(payload)\n continue\n if op == 0x0:\n if opcode is None:\n raise ConnectionError(\"stray continuation frame\")\n parts.append(payload)\n if fin:\n return (\"data\", opcode, b\"\".join(parts))\n continue\n raise ConnectionError(f\"unsupported opcode {op}\")\n\ndef _is_daemon_response(payload: bytes, expect_id=None, known_ids=None) -> bool:\n \"\"\"True only for the matching JSON-RPC response/error, never a notification.\n\n The first-RPC timer must survive unrelated daemon notifications and foreign\n responses: only the response/error whose id matches the client's initialize\n (or, before it is known, any id the client already sent) proves the first\n RPC got an answer. Timeouts clear it via the deadline, never via data.\n \"\"\"\n try:\n text = payload.decode(\"utf-8\").strip()\n msg = json.loads(text)\n except Exception:\n return False\n if not isinstance(msg, dict):\n return False\n rid = msg.get(\"id\")\n if rid is None or \"method\" in msg or (\"result\" not in msg and \"error\" not in msg):\n return False\n if expect_id is not None:\n return rid == expect_id\n if known_ids:\n return rid in known_ids\n return False\n\ndef _bounded_stdout_write(data: bytes, timeout: float = IO_TIMEOUT_S) -> None:\n \"\"\"Write one daemon frame to stdout without hanging on a non-reading peer.\n\n Waits for writability under the mid-session write budget and writes via\n os.write in a loop, so a Desktop that stopped reading cannot hang the\n bridge and then exit 1 on dirty stdout.\n \"\"\"\n if timeout is None:\n timeout = IO_TIMEOUT_S\n if timeout <= 0:\n raise TimeoutError(\"stdout budget expired\")\n fd = sys.stdout.fileno()\n deadline = time.monotonic() + timeout\n was_blocking = os.get_blocking(fd)\n os.set_blocking(fd, False)\n try:\n view = memoryview(data)\n while view:\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"stdout write timed out\")\n _, writable, _ = select.select([], [fd], [], remaining)\n if not writable:\n raise TimeoutError(\"stdout write timed out\")\n try:\n n = os.write(fd, view)\n except BlockingIOError:\n continue\n view = view[n:]\n finally:\n os.set_blocking(fd, was_blocking)\n\ndef stdout_writer(sock: socket.socket, recv, send_pong, done: threading.Event, first_msg: threading.Event, output_forwarded: threading.Event, client_ids=None, init_state=None) -> None:\n try:\n while True:\n kind, opcode, payload = read_message(recv, send_pong)\n if kind == \"close\":\n log(\"daemon WS close\")\n return\n if opcode == 0x1:\n # Desktop StdioConnection expects newline-delimited JSON text\n if not payload.endswith(b\"\\n\"):\n payload += b\"\\n\"\n # Commit BEFORE writing: once a daemon byte is about to hit\n # stdout, the session is dirty and the fallback must never run,\n # even if the write itself blocks or fails.\n output_forwarded.set()\n _bounded_stdout_write(payload)\n if opcode == 0x1:\n expect = (init_state or {}).get(\"initialize_id\") if isinstance(init_state, dict) else None\n if _is_daemon_response(payload, expect, client_ids or ()):\n first_msg.set()\n except Exception as e:\n log(f\"stdout_writer exit: {e}\")\n finally:\n done.set()\n\ndef main() -> int:\n log(f\"start sock={SOCK}\")\n try:\n sock, pending = ws_connect(SOCK)\n except Exception as e:\n log(f\"connect failed: {e}\")\n sys.stderr.write(f\"codex-stdio-to-daemon-ws: {e}\\n\")\n return EXIT_PRE_STDIO\n log(\"connected\")\n connected_at = time.monotonic()\n first_deadline = connected_at + FIRST_MESSAGE_TIMEOUT_S\n send_lock = threading.Lock()\n done = threading.Event()\n first_msg = threading.Event()\n output_forwarded = threading.Event()\n client_ids = set()\n init_state = {\"initialize_id\": None}\n\n def _send_timeout() -> float:\n # Before the first matching daemon response the remaining first-RPC\n # budget bounds every send; after it each send gets the mid-session\n # write budget. Idle reads are never timed out.\n if not first_msg.is_set():\n return max(0.0, first_deadline - time.monotonic())\n return IO_TIMEOUT_S\n\n def _cleanup_timeout() -> float:\n # Timeout cleanup stays within the expired session budget: once the\n # first-RPC deadline has passed, close directly instead of opening\n # fresh windows that let a short budget take several timeout periods.\n if not first_msg.is_set():\n return max(0.0, first_deadline - time.monotonic())\n return CONNECT_TIMEOUT_S\n\n def send_pong(payload: bytes) -> None:\n locked_send(sock, send_lock, payload, opcode=0xA, timeout=_send_timeout())\n recv = _buffered_reader(sock, pending)\n t = threading.Thread(target=stdout_writer, args=(sock, recv, send_pong, done, first_msg, output_forwarded, client_ids, init_state), daemon=True)\n t.start()\n # Byte-precise commit point: the fallback may run only while zero stdin\n # bytes have left the pipe and no daemon payload was committed to stdout.\n # Reads use os.read into a userspace line buffer (buffered readline could\n # readahead past the commit point), and every byte read counts -- even a\n # blank line. first_msg tracks the matching daemon response/error, never\n # a notification.\n stdin_bytes = 0\n pending_in = b\"\"\n clean_eof = False\n try:\n fd = sys.stdin.fileno()\n while not done.is_set():\n if not first_msg.is_set() and time.monotonic() >= first_deadline:\n log(\"first daemon message timed out\")\n break\n ready, _, _ = select.select([fd], [], [], 0.5)\n if not ready:\n continue\n chunk = os.read(fd, 65536)\n if not chunk:\n clean_eof = True\n break\n stdin_bytes += len(chunk)\n pending_in += chunk\n while b\"\\n\" in pending_in:\n line, _, pending_in = pending_in.partition(b\"\\n\")\n line = line.strip()\n if not line:\n continue\n try:\n try:\n obj = json.loads(line.decode(\"utf-8\"))\n except Exception:\n obj = None\n if isinstance(obj, dict) and obj.get(\"id\") is not None:\n client_ids.add(obj.get(\"id\"))\n if obj.get(\"method\") == \"initialize\" and init_state[\"initialize_id\"] is None:\n init_state[\"initialize_id\"] = obj.get(\"id\")\n except Exception:\n pass\n try:\n locked_send(sock, send_lock, line, timeout=_send_timeout())\n except TimeoutError as e:\n log(f\"stdin forward timed out: {e}\")\n break\n except Exception as e:\n log(f\"stdin forward failed: {e}\")\n break\n else:\n continue\n break\n except Exception as e:\n log(f\"stdin loop exit: {e}\")\n # Flush any buffered EOF-tail data BEFORE the WS Close: sending Close\n # first would let the daemon discard the tail.\n if clean_eof:\n tail = pending_in.strip()\n if tail and not done.is_set():\n try:\n locked_send(sock, send_lock, tail, timeout=_cleanup_timeout())\n except Exception as e:\n log(f\"eof flush failed: {e}\")\n try:\n locked_send(sock, send_lock, b\"\", opcode=0x8, timeout=_cleanup_timeout())\n except Exception as e:\n log(f\"close send failed: {e}\")\n try:\n try:\n sock.shutdown(socket.SHUT_RDWR)\n except OSError:\n pass\n sock.close()\n except OSError:\n pass\n # Bound termination: wake the reader so no thread is left hung on recv.\n t.join(timeout=0.5)\n # A reader still exiting could commit output after this check. In that\n # case pristine stdio cannot be proven, so never permit fallback.\n if t.is_alive():\n output_forwarded.set()\n if clean_eof and (stdin_bytes or output_forwarded.is_set()):\n log(\"exit\")\n return EXIT_SERVED\n # Reader died, first RPC timed out, post-upgrade I/O timed out, or stdin\n # broke: fail open only while stdio is still pristine -- no stdin byte\n # ever consumed AND no daemon payload ever committed to stdout. Committed\n # output forces a mid-session exit so Desktop reconnects instead of\n # falling back to stock Codex on a corrupted stream.\n rc = EXIT_MID_SESSION if (stdin_bytes or output_forwarded.is_set()) else EXIT_PRE_STDIO\n log(f\"exit rc={rc}\")\n return rc\n\nif __name__ == \"__main__\":\n raise SystemExit(main())\n"; __webpack_require__.d(__webpack_exports__, {}, { w: BRIDGE_SOURCE }); @@ -47254,19 +47254,30 @@ var __webpack_modules__ = { let daemon = false; let proxy = false; let generate = false; + let oneShot = false; for (const arg of argv ?? []){ + if ([ + "--print-startup-config", + "--startup-sign-out", + "help", + "--help", + "-h", + "--version", + "-V" + ].includes(arg)) oneShot = true; if (arg === "app-server") appServer = true; else if (arg === "daemon") daemon = true; else if (arg === "proxy") proxy = true; else if (arg === "generate-ts" || arg === "generate-json-schema") generate = true; } - return appServer && !daemon && !proxy && !generate; + return appServer && !daemon && !proxy && !generate && !oneShot; } function renderWrapperScript({ realPath, bridgePath, wrapperLogPath, codexHome, bridgeLogPath }) { return `#!/bin/bash # Installed by \`gbot codex desktop-shim install\`. Rewrites ChatGPT Desktop's # \`codex ... app-server\` spawn into a stdio<->WebSocket bridge onto the managed # daemon's control socket. Reversible: \`gbot codex desktop-shim uninstall\`. +# Startup configuration, sign-out, help and version requests use the real CLI. # The daemon is kept up by the LaunchAgent login script, not here: this hot path # never starts the daemon itself, so a wedged daemon lock cannot hang # Desktop. Fail-open runs ONLY while stdio is still pristine (no stdin @@ -47299,9 +47310,10 @@ if [[ ! -x "$REAL" ]]; then fi fi -has_app_server=0; has_daemon=0; has_proxy=0; has_generate=0 +has_app_server=0; has_daemon=0; has_proxy=0; has_generate=0; has_one_shot=0 for a in "$@"; do case "$a" in + --print-startup-config|--startup-sign-out|help|--help|-h|--version|-V) has_one_shot=1 ;; app-server) has_app_server=1 ;; daemon) has_daemon=1 ;; proxy) has_proxy=1 ;; @@ -47315,7 +47327,7 @@ preflight() { python3 -c 'import os,socket,sys; s=socket.socket(socket.AF_UNIX,socket.SOCK_STREAM); s.settimeout(float(os.environ.get("CODEX_DESKTOP_PREFLIGHT_TIMEOUT","3"))); s.connect(sys.argv[1]); s.close()' "$SOCK" 2>/dev/null } -if [[ "$has_app_server" -eq 1 && "$has_daemon" -eq 0 && "$has_proxy" -eq 0 && "$has_generate" -eq 0 ]]; then +if [[ "$has_app_server" -eq 1 && "$has_daemon" -eq 0 && "$has_proxy" -eq 0 && "$has_generate" -eq 0 && "$has_one_shot" -eq 0 ]]; then if [[ -f "$BRIDGE" ]] && command -v python3 >/dev/null 2>&1; then if preflight; then echo "$(ts) rewrite -> stdio-ws bridge" >>"$LOG" 2>/dev/null || true diff --git a/artifact/mcp/mcp-grok-bot-b8c2461e-flight.mjs b/artifact/mcp/mcp-grok-bot-b8c2461e-flight.mjs index 03b77df..3770a38 100644 --- a/artifact/mcp/mcp-grok-bot-b8c2461e-flight.mjs +++ b/artifact/mcp/mcp-grok-bot-b8c2461e-flight.mjs @@ -27448,19 +27448,30 @@ var __webpack_modules__ = { let daemon = false; let proxy = false; let generate = false; + let oneShot = false; for (const arg of argv ?? []){ + if ([ + "--print-startup-config", + "--startup-sign-out", + "help", + "--help", + "-h", + "--version", + "-V" + ].includes(arg)) oneShot = true; if (arg === "app-server") appServer = true; else if (arg === "daemon") daemon = true; else if (arg === "proxy") proxy = true; else if (arg === "generate-ts" || arg === "generate-json-schema") generate = true; } - return appServer && !daemon && !proxy && !generate; + return appServer && !daemon && !proxy && !generate && !oneShot; } function renderWrapperScript({ realPath, bridgePath, wrapperLogPath, codexHome, bridgeLogPath }) { return `#!/bin/bash # Installed by \`gbot codex desktop-shim install\`. Rewrites ChatGPT Desktop's # \`codex ... app-server\` spawn into a stdio<->WebSocket bridge onto the managed # daemon's control socket. Reversible: \`gbot codex desktop-shim uninstall\`. +# Startup configuration, sign-out, help and version requests use the real CLI. # The daemon is kept up by the LaunchAgent login script, not here: this hot path # never starts the daemon itself, so a wedged daemon lock cannot hang # Desktop. Fail-open runs ONLY while stdio is still pristine (no stdin @@ -27493,9 +27504,10 @@ if [[ ! -x "$REAL" ]]; then fi fi -has_app_server=0; has_daemon=0; has_proxy=0; has_generate=0 +has_app_server=0; has_daemon=0; has_proxy=0; has_generate=0; has_one_shot=0 for a in "$@"; do case "$a" in + --print-startup-config|--startup-sign-out|help|--help|-h|--version|-V) has_one_shot=1 ;; app-server) has_app_server=1 ;; daemon) has_daemon=1 ;; proxy) has_proxy=1 ;; @@ -27509,7 +27521,7 @@ preflight() { python3 -c 'import os,socket,sys; s=socket.socket(socket.AF_UNIX,socket.SOCK_STREAM); s.settimeout(float(os.environ.get("CODEX_DESKTOP_PREFLIGHT_TIMEOUT","3"))); s.connect(sys.argv[1]); s.close()' "$SOCK" 2>/dev/null } -if [[ "$has_app_server" -eq 1 && "$has_daemon" -eq 0 && "$has_proxy" -eq 0 && "$has_generate" -eq 0 ]]; then +if [[ "$has_app_server" -eq 1 && "$has_daemon" -eq 0 && "$has_proxy" -eq 0 && "$has_generate" -eq 0 && "$has_one_shot" -eq 0 ]]; then if [[ -f "$BRIDGE" ]] && command -v python3 >/dev/null 2>&1; then if preflight; then echo "$(ts) rewrite -> stdio-ws bridge" >>"$LOG" 2>/dev/null || true diff --git a/artifact/mcp/mcp-grok-bot-b8c2461e.mjs b/artifact/mcp/mcp-grok-bot-b8c2461e.mjs index 3e69638..14818fe 100644 --- a/artifact/mcp/mcp-grok-bot-b8c2461e.mjs +++ b/artifact/mcp/mcp-grok-bot-b8c2461e.mjs @@ -64663,19 +64663,30 @@ var __webpack_modules__ = { let daemon = false; let proxy = false; let generate = false; + let oneShot = false; for (const arg of argv ?? []){ + if ([ + "--print-startup-config", + "--startup-sign-out", + "help", + "--help", + "-h", + "--version", + "-V" + ].includes(arg)) oneShot = true; if (arg === "app-server") appServer = true; else if (arg === "daemon") daemon = true; else if (arg === "proxy") proxy = true; else if (arg === "generate-ts" || arg === "generate-json-schema") generate = true; } - return appServer && !daemon && !proxy && !generate; + return appServer && !daemon && !proxy && !generate && !oneShot; } function renderWrapperScript({ realPath, bridgePath, wrapperLogPath, codexHome, bridgeLogPath }) { return `#!/bin/bash # Installed by \`gbot codex desktop-shim install\`. Rewrites ChatGPT Desktop's # \`codex ... app-server\` spawn into a stdio<->WebSocket bridge onto the managed # daemon's control socket. Reversible: \`gbot codex desktop-shim uninstall\`. +# Startup configuration, sign-out, help and version requests use the real CLI. # The daemon is kept up by the LaunchAgent login script, not here: this hot path # never starts the daemon itself, so a wedged daemon lock cannot hang # Desktop. Fail-open runs ONLY while stdio is still pristine (no stdin @@ -64708,9 +64719,10 @@ if [[ ! -x "$REAL" ]]; then fi fi -has_app_server=0; has_daemon=0; has_proxy=0; has_generate=0 +has_app_server=0; has_daemon=0; has_proxy=0; has_generate=0; has_one_shot=0 for a in "$@"; do case "$a" in + --print-startup-config|--startup-sign-out|help|--help|-h|--version|-V) has_one_shot=1 ;; app-server) has_app_server=1 ;; daemon) has_daemon=1 ;; proxy) has_proxy=1 ;; @@ -64724,7 +64736,7 @@ preflight() { python3 -c 'import os,socket,sys; s=socket.socket(socket.AF_UNIX,socket.SOCK_STREAM); s.settimeout(float(os.environ.get("CODEX_DESKTOP_PREFLIGHT_TIMEOUT","3"))); s.connect(sys.argv[1]); s.close()' "$SOCK" 2>/dev/null } -if [[ "$has_app_server" -eq 1 && "$has_daemon" -eq 0 && "$has_proxy" -eq 0 && "$has_generate" -eq 0 ]]; then +if [[ "$has_app_server" -eq 1 && "$has_daemon" -eq 0 && "$has_proxy" -eq 0 && "$has_generate" -eq 0 && "$has_one_shot" -eq 0 ]]; then if [[ -f "$BRIDGE" ]] && command -v python3 >/dev/null 2>&1; then if preflight; then echo "$(ts) rewrite -> stdio-ws bridge" >>"$LOG" 2>/dev/null || true diff --git a/artifact/scripts/gbot-relay.mjs b/artifact/scripts/gbot-relay.mjs index 26a519a..72202a7 100644 --- a/artifact/scripts/gbot-relay.mjs +++ b/artifact/scripts/gbot-relay.mjs @@ -24638,19 +24638,30 @@ var __webpack_modules__ = { let daemon = false; let proxy = false; let generate = false; + let oneShot = false; for (const arg of argv ?? []){ + if ([ + "--print-startup-config", + "--startup-sign-out", + "help", + "--help", + "-h", + "--version", + "-V" + ].includes(arg)) oneShot = true; if (arg === "app-server") appServer = true; else if (arg === "daemon") daemon = true; else if (arg === "proxy") proxy = true; else if (arg === "generate-ts" || arg === "generate-json-schema") generate = true; } - return appServer && !daemon && !proxy && !generate; + return appServer && !daemon && !proxy && !generate && !oneShot; } function renderWrapperScript({ realPath, bridgePath, wrapperLogPath, codexHome, bridgeLogPath }) { return `#!/bin/bash # Installed by \`gbot codex desktop-shim install\`. Rewrites ChatGPT Desktop's # \`codex ... app-server\` spawn into a stdio<->WebSocket bridge onto the managed # daemon's control socket. Reversible: \`gbot codex desktop-shim uninstall\`. +# Startup configuration, sign-out, help and version requests use the real CLI. # The daemon is kept up by the LaunchAgent login script, not here: this hot path # never starts the daemon itself, so a wedged daemon lock cannot hang # Desktop. Fail-open runs ONLY while stdio is still pristine (no stdin @@ -24683,9 +24694,10 @@ if [[ ! -x "$REAL" ]]; then fi fi -has_app_server=0; has_daemon=0; has_proxy=0; has_generate=0 +has_app_server=0; has_daemon=0; has_proxy=0; has_generate=0; has_one_shot=0 for a in "$@"; do case "$a" in + --print-startup-config|--startup-sign-out|help|--help|-h|--version|-V) has_one_shot=1 ;; app-server) has_app_server=1 ;; daemon) has_daemon=1 ;; proxy) has_proxy=1 ;; @@ -24699,7 +24711,7 @@ preflight() { python3 -c 'import os,socket,sys; s=socket.socket(socket.AF_UNIX,socket.SOCK_STREAM); s.settimeout(float(os.environ.get("CODEX_DESKTOP_PREFLIGHT_TIMEOUT","3"))); s.connect(sys.argv[1]); s.close()' "$SOCK" 2>/dev/null } -if [[ "$has_app_server" -eq 1 && "$has_daemon" -eq 0 && "$has_proxy" -eq 0 && "$has_generate" -eq 0 ]]; then +if [[ "$has_app_server" -eq 1 && "$has_daemon" -eq 0 && "$has_proxy" -eq 0 && "$has_generate" -eq 0 && "$has_one_shot" -eq 0 ]]; then if [[ -f "$BRIDGE" ]] && command -v python3 >/dev/null 2>&1; then if preflight; then echo "$(ts) rewrite -> stdio-ws bridge" >>"$LOG" 2>/dev/null || true diff --git a/src/core/desktop-shim-bridge.js b/src/core/desktop-shim-bridge.js index 7b3481e..2bbc7c4 100644 --- a/src/core/desktop-shim-bridge.js +++ b/src/core/desktop-shim-bridge.js @@ -31,4 +31,4 @@ // Desktop stdio hangs with stock `codex app-server proxy`, so this custom // bridge is required. Install writes it to the CODEX_HOME bin dir. Never reads // Desktop private pipes (CODEX_APP_TOOLS_PIPE_PATH, /tmp/codex-browser-use/). -export const BRIDGE_SOURCE = "#!/usr/bin/env python3\n\"\"\"Stdio JSONL (Desktop) <-> WebSocket-over-unix (managed app-server daemon).\nReversible: remove CODEX_CLI_PATH wrapper. Does not read Desktop private pipes.\n\"\"\"\nfrom __future__ import annotations\nimport base64, hashlib, json, os, select, socket, struct, sys, threading, time\n\nSOCK = os.environ.get(\n \"CODEX_APP_SERVER_SOCK\",\n os.path.expanduser(\"~/.codex/app-server-control/app-server-control.sock\"),\n)\nLOG = os.environ.get(\"CODEX_STDIO_BRIDGE_LOG\", os.path.expanduser(\"~/.codex/bin/codex-stdio-to-daemon-ws.log\"))\nWS_GUID = \"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\"\n\n# Wrapper contract: 0 served the session, 1 failed while stdio was still\n# pristine -- no stdin byte consumed and no stdout frame committed (wrapper\n# falls back to real Codex), 2 failed mid-session (wrapper exits promptly so\n# Desktop reconnects; the fallback must never run on half-consumed stdin or\n# dirty stdout).\nEXIT_SERVED = 0\nEXIT_PRE_STDIO = 1\nEXIT_MID_SESSION = 2\n\nCONNECT_TIMEOUT_S = float(os.environ.get(\"CODEX_BRIDGE_CONNECT_TIMEOUT\", \"10\"))\n# Bound from connect to the matching first daemon response: a daemon that\n# completes the upgrade but never answers the first RPC must not hold Desktop\n# past this. Only the matching initialize response/error (or the deadline\n# itself) clears it; notifications never do.\nFIRST_MESSAGE_TIMEOUT_S = float(os.environ.get(\"CODEX_BRIDGE_FIRST_MESSAGE_TIMEOUT\", \"30\"))\n# Bound on every mid-session socket send and stdout write: a wedged or\n# non-reading peer must not hang Desktop past this. Reads after a successful\n# init block with no timeout so healthy idle sessions survive silence.\nIO_TIMEOUT_S = float(os.environ.get(\"CODEX_BRIDGE_IO_TIMEOUT\", \"30\"))\n\ndef log(msg: str) -> None:\n try:\n with open(LOG, \"a\") as f:\n f.write(time.strftime(\"%Y-%m-%dT%H:%M:%SZ\", time.gmtime()) + \" \" + msg + \"\\n\")\n except OSError:\n pass\n\ndef ws_connect(path: str, timeout: float = CONNECT_TIMEOUT_S):\n \"\"\"Connect + HTTP upgrade under one absolute monotonic deadline.\n\n Returns (socket, trailing) where trailing holds bytes coalesced after the\n upgrade headers (TCP may deliver the 101 and the first WS frame together;\n discarding them would lose the first message). Requires HTTP/1.1 101 plus\n a matching Sec-WebSocket-Accept so a 200 (or any non-101) fails fast even\n when the accept hash happens to be valid.\n \"\"\"\n deadline = time.monotonic() + timeout\n s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)\n s.settimeout(timeout)\n try:\n s.connect(path)\n key = base64.b64encode(os.urandom(16)).decode()\n expected = base64.b64encode(hashlib.sha1((key + WS_GUID).encode()).digest()).decode()\n req = (\n f\"GET /rpc HTTP/1.1\\r\\nHost: localhost\\r\\nUpgrade: websocket\\r\\n\"\n f\"Connection: Upgrade\\r\\nSec-WebSocket-Key: {key}\\r\\nSec-WebSocket-Version: 13\\r\\n\\r\\n\"\n ).encode()\n s.settimeout(max(0.0, deadline - time.monotonic()))\n s.sendall(req)\n buf = b\"\"\n while True:\n if b\"\\r\\n\\r\\n\" in buf:\n break\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"WebSocket upgrade timed out\")\n if len(buf) > 65536:\n raise ConnectionError(\"WS upgrade headers too large\")\n s.settimeout(remaining)\n chunk = s.recv(4096)\n if not chunk:\n raise ConnectionError(\"daemon closed during WS upgrade\")\n buf += chunk\n head, _, trailing = buf.partition(b\"\\r\\n\\r\\n\")\n lines = head.split(b\"\\r\\n\")\n if not lines or lines[0].split(b\" \", 2)[:2] != [b\"HTTP/1.1\", b\"101\"]:\n raise ConnectionError(f\"WS upgrade failed: {head[:200]!r}\")\n accept = None\n for line in lines[1:]:\n name, sep, value = line.partition(b\":\")\n if sep and name.strip().lower() == b\"sec-websocket-accept\":\n accept = value.strip().decode(\"latin-1\")\n if accept != expected:\n raise ConnectionError(\"WS upgrade accept mismatch\")\n except Exception:\n try:\n s.close()\n except OSError:\n pass\n raise\n # Keep one socket mode across reader and writer threads. Reads wait\n # indefinitely in select; sends use their own absolute deadline.\n s.setblocking(False)\n return s, trailing\n\ndef mask_send(sock: socket.socket, payload: bytes, opcode: int = 1, deadline=None) -> None:\n mask = os.urandom(4)\n ln = len(payload)\n if ln < 126:\n hdr = bytes([0x80 | opcode, 0x80 | ln])\n elif ln < 65536:\n hdr = bytes([0x80 | opcode, 0x80 | 126]) + struct.pack(\"!H\", ln)\n else:\n hdr = bytes([0x80 | opcode, 0x80 | 127]) + struct.pack(\"!Q\", ln)\n view = memoryview(hdr + mask + bytes(b ^ mask[i % 4] for i, b in enumerate(payload)))\n while view:\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"socket write timed out\")\n _, writable, _ = select.select([], [sock], [], remaining)\n if not writable:\n raise TimeoutError(\"socket write timed out\")\n try:\n n = sock.send(view)\n except BlockingIOError:\n continue\n if not n:\n raise ConnectionError(\"socket closed during write\")\n view = view[n:]\n\ndef locked_send(sock: socket.socket, lock: threading.Lock, payload: bytes, opcode: int = 1, timeout: float = CONNECT_TIMEOUT_S) -> None:\n \"\"\"Serialized send sharing one monotonic budget across lock wait + send.\n\n A non-reading peer must never wedge pong writes or timeout cleanup: on\n expiry raise TimeoutError so the caller closes and exits without hanging.\n \"\"\"\n if timeout is None:\n timeout = CONNECT_TIMEOUT_S\n deadline = time.monotonic() + timeout\n if timeout <= 0:\n raise TimeoutError(\"send budget expired\")\n if not lock.acquire(timeout=timeout):\n raise TimeoutError(\"send lock timed out\")\n try:\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"send budget expired\")\n mask_send(sock, payload, opcode=opcode, deadline=deadline)\n finally:\n lock.release()\n\ndef _buffered_reader(sock: socket.socket, initial: bytes = b\"\"):\n buf = bytearray(initial)\n def recv(n):\n while len(buf) < n:\n select.select([sock], [], [])\n try:\n chunk = sock.recv(n - len(buf))\n except BlockingIOError:\n continue\n if not chunk:\n raise ConnectionError(\"eof\")\n buf.extend(chunk)\n out = bytes(buf[:n])\n del buf[:n]\n return out\n return recv\n\ndef read_frame(recv):\n hdr = recv(2)\n fin = bool(hdr[0] & 0x80)\n opcode = hdr[0] & 0x0F\n masked = bool(hdr[1] & 0x80)\n ln = hdr[1] & 0x7F\n if ln == 126:\n ln = struct.unpack(\"!H\", recv(2))[0]\n elif ln == 127:\n ln = struct.unpack(\"!Q\", recv(8))[0]\n mask = recv(4) if masked else b\"\"\n payload = recv(ln)\n if masked:\n payload = bytes(b ^ mask[i % 4] for i, b in enumerate(payload))\n return fin, opcode, payload\n\ndef read_message(recv, send_pong):\n \"\"\"Next complete data message, assembling continuation frames.\n\n Control frames are handled inline (pong answered, close reported) so a\n fragmented message split around a ping still reassembles.\n \"\"\"\n opcode = None\n parts = []\n while True:\n fin, op, payload = read_frame(recv)\n if op == 0x8:\n return (\"close\", None, b\"\")\n if op == 0x9:\n send_pong(payload)\n continue\n if op == 0xA:\n continue\n if op in (0x1, 0x2):\n if opcode is not None:\n raise ConnectionError(\"data frame inside fragmented message\")\n if fin:\n return (\"data\", op, payload)\n opcode = op\n parts.append(payload)\n continue\n if op == 0x0:\n if opcode is None:\n raise ConnectionError(\"stray continuation frame\")\n parts.append(payload)\n if fin:\n return (\"data\", opcode, b\"\".join(parts))\n continue\n raise ConnectionError(f\"unsupported opcode {op}\")\n\ndef _is_daemon_response(payload: bytes, expect_id=None, known_ids=None) -> bool:\n \"\"\"True only for the matching JSON-RPC response/error, never a notification.\n\n The first-RPC timer must survive unrelated daemon notifications and foreign\n responses: only the response/error whose id matches the client's initialize\n (or, before it is known, any id the client already sent) proves the first\n RPC got an answer. Timeouts clear it via the deadline, never via data.\n \"\"\"\n try:\n text = payload.decode(\"utf-8\").strip()\n msg = json.loads(text)\n except Exception:\n return False\n if not isinstance(msg, dict):\n return False\n rid = msg.get(\"id\")\n if rid is None or \"method\" in msg or (\"result\" not in msg and \"error\" not in msg):\n return False\n if expect_id is not None:\n return rid == expect_id\n if known_ids:\n return rid in known_ids\n return False\n\ndef _bounded_stdout_write(data: bytes, timeout: float = IO_TIMEOUT_S) -> None:\n \"\"\"Write one daemon frame to stdout without hanging on a non-reading peer.\n\n Waits for writability under the mid-session write budget and writes via\n os.write in a loop, so a Desktop that stopped reading cannot hang the\n bridge and then exit 1 on dirty stdout.\n \"\"\"\n if timeout is None:\n timeout = IO_TIMEOUT_S\n if timeout <= 0:\n raise TimeoutError(\"stdout budget expired\")\n fd = sys.stdout.fileno()\n deadline = time.monotonic() + timeout\n was_blocking = os.get_blocking(fd)\n os.set_blocking(fd, False)\n try:\n view = memoryview(data)\n while view:\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"stdout write timed out\")\n _, writable, _ = select.select([], [fd], [], remaining)\n if not writable:\n raise TimeoutError(\"stdout write timed out\")\n try:\n n = os.write(fd, view)\n except BlockingIOError:\n continue\n view = view[n:]\n finally:\n os.set_blocking(fd, was_blocking)\n\ndef stdout_writer(sock: socket.socket, recv, send_pong, done: threading.Event, first_msg: threading.Event, output_forwarded: threading.Event, client_ids=None, init_state=None) -> None:\n try:\n while True:\n kind, opcode, payload = read_message(recv, send_pong)\n if kind == \"close\":\n log(\"daemon WS close\")\n return\n if opcode == 0x1:\n # Desktop StdioConnection expects newline-delimited JSON text\n if not payload.endswith(b\"\\n\"):\n payload += b\"\\n\"\n # Commit BEFORE writing: once a daemon byte is about to hit\n # stdout, the session is dirty and the fallback must never run,\n # even if the write itself blocks or fails.\n output_forwarded.set()\n _bounded_stdout_write(payload)\n if opcode == 0x1:\n expect = (init_state or {}).get(\"initialize_id\") if isinstance(init_state, dict) else None\n if _is_daemon_response(payload, expect, client_ids or ()):\n first_msg.set()\n except Exception as e:\n log(f\"stdout_writer exit: {e}\")\n finally:\n done.set()\n\ndef main() -> int:\n log(f\"start sock={SOCK}\")\n try:\n sock, pending = ws_connect(SOCK)\n except Exception as e:\n log(f\"connect failed: {e}\")\n sys.stderr.write(f\"codex-stdio-to-daemon-ws: {e}\\n\")\n return EXIT_PRE_STDIO\n log(\"connected\")\n connected_at = time.monotonic()\n first_deadline = connected_at + FIRST_MESSAGE_TIMEOUT_S\n send_lock = threading.Lock()\n done = threading.Event()\n first_msg = threading.Event()\n output_forwarded = threading.Event()\n client_ids = set()\n init_state = {\"initialize_id\": None}\n\n def _send_timeout() -> float:\n # Before the first matching daemon response the remaining first-RPC\n # budget bounds every send; after it each send gets the mid-session\n # write budget. Idle reads are never timed out.\n if not first_msg.is_set():\n return max(0.0, first_deadline - time.monotonic())\n return IO_TIMEOUT_S\n\n def _cleanup_timeout() -> float:\n # Timeout cleanup stays within the expired session budget: once the\n # first-RPC deadline has passed, close directly instead of opening\n # fresh windows that let a short budget take several timeout periods.\n if not first_msg.is_set():\n return max(0.0, first_deadline - time.monotonic())\n return CONNECT_TIMEOUT_S\n\n def send_pong(payload: bytes) -> None:\n locked_send(sock, send_lock, payload, opcode=0xA, timeout=_send_timeout())\n recv = _buffered_reader(sock, pending)\n t = threading.Thread(target=stdout_writer, args=(sock, recv, send_pong, done, first_msg, output_forwarded, client_ids, init_state), daemon=True)\n t.start()\n # Byte-precise commit point: the fallback may run only while zero stdin\n # bytes have left the pipe and no daemon payload was committed to stdout.\n # Reads use os.read into a userspace line buffer (buffered readline could\n # readahead past the commit point), and every byte read counts -- even a\n # blank line. first_msg tracks the matching daemon response/error, never\n # a notification.\n stdin_bytes = 0\n pending_in = b\"\"\n clean_eof = False\n try:\n fd = sys.stdin.fileno()\n while not done.is_set():\n if not first_msg.is_set() and time.monotonic() >= first_deadline:\n log(\"first daemon message timed out\")\n break\n ready, _, _ = select.select([fd], [], [], 0.5)\n if not ready:\n continue\n chunk = os.read(fd, 65536)\n if not chunk:\n clean_eof = True\n break\n stdin_bytes += len(chunk)\n pending_in += chunk\n while b\"\\n\" in pending_in:\n line, _, pending_in = pending_in.partition(b\"\\n\")\n line = line.strip()\n if not line:\n continue\n try:\n try:\n obj = json.loads(line.decode(\"utf-8\"))\n except Exception:\n obj = None\n if isinstance(obj, dict) and obj.get(\"id\") is not None:\n client_ids.add(obj.get(\"id\"))\n if obj.get(\"method\") == \"initialize\" and init_state[\"initialize_id\"] is None:\n init_state[\"initialize_id\"] = obj.get(\"id\")\n except Exception:\n pass\n try:\n locked_send(sock, send_lock, line, timeout=_send_timeout())\n except TimeoutError as e:\n log(f\"stdin forward timed out: {e}\")\n break\n except Exception as e:\n log(f\"stdin forward failed: {e}\")\n break\n else:\n continue\n break\n except Exception as e:\n log(f\"stdin loop exit: {e}\")\n # Flush any buffered EOF-tail data BEFORE the WS Close: sending Close\n # first would let the daemon discard the tail.\n if clean_eof:\n tail = pending_in.strip()\n if tail and not done.is_set():\n try:\n locked_send(sock, send_lock, tail, timeout=_cleanup_timeout())\n except Exception as e:\n log(f\"eof flush failed: {e}\")\n try:\n locked_send(sock, send_lock, b\"\", opcode=0x8, timeout=_cleanup_timeout())\n except Exception as e:\n log(f\"close send failed: {e}\")\n try:\n try:\n sock.shutdown(socket.SHUT_RDWR)\n except OSError:\n pass\n sock.close()\n except OSError:\n pass\n # Bound termination: wake the reader so no thread is left hung on recv.\n t.join(timeout=0.5)\n # A reader still exiting could commit output after this check. In that\n # case pristine stdio cannot be proven, so never permit fallback.\n if t.is_alive():\n output_forwarded.set()\n if clean_eof:\n log(\"exit\")\n return EXIT_SERVED\n # Reader died, first RPC timed out, post-upgrade I/O timed out, or stdin\n # broke: fail open only while stdio is still pristine -- no stdin byte\n # ever consumed AND no daemon payload ever committed to stdout. Committed\n # output forces a mid-session exit so Desktop reconnects instead of\n # falling back to stock Codex on a corrupted stream.\n rc = EXIT_MID_SESSION if (stdin_bytes or output_forwarded.is_set()) else EXIT_PRE_STDIO\n log(f\"exit rc={rc}\")\n return rc\n\nif __name__ == \"__main__\":\n raise SystemExit(main())\n"; +export const BRIDGE_SOURCE = "#!/usr/bin/env python3\n\"\"\"Stdio JSONL (Desktop) <-> WebSocket-over-unix (managed app-server daemon).\nReversible: remove CODEX_CLI_PATH wrapper. Does not read Desktop private pipes.\n\"\"\"\nfrom __future__ import annotations\nimport base64, hashlib, json, os, select, socket, struct, sys, threading, time\n\nSOCK = os.environ.get(\n \"CODEX_APP_SERVER_SOCK\",\n os.path.expanduser(\"~/.codex/app-server-control/app-server-control.sock\"),\n)\nLOG = os.environ.get(\"CODEX_STDIO_BRIDGE_LOG\", os.path.expanduser(\"~/.codex/bin/codex-stdio-to-daemon-ws.log\"))\nWS_GUID = \"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\"\n\n# Wrapper contract: 0 served the session, 1 failed while stdio was still\n# pristine -- no stdin byte consumed and no stdout frame committed (wrapper\n# falls back to real Codex), 2 failed mid-session (wrapper exits promptly so\n# Desktop reconnects; the fallback must never run on half-consumed stdin or\n# dirty stdout).\nEXIT_SERVED = 0\nEXIT_PRE_STDIO = 1\nEXIT_MID_SESSION = 2\n\nCONNECT_TIMEOUT_S = float(os.environ.get(\"CODEX_BRIDGE_CONNECT_TIMEOUT\", \"10\"))\n# Bound from connect to the matching first daemon response: a daemon that\n# completes the upgrade but never answers the first RPC must not hold Desktop\n# past this. Only the matching initialize response/error (or the deadline\n# itself) clears it; notifications never do.\nFIRST_MESSAGE_TIMEOUT_S = float(os.environ.get(\"CODEX_BRIDGE_FIRST_MESSAGE_TIMEOUT\", \"30\"))\n# Bound on every mid-session socket send and stdout write: a wedged or\n# non-reading peer must not hang Desktop past this. Reads after a successful\n# init block with no timeout so healthy idle sessions survive silence.\nIO_TIMEOUT_S = float(os.environ.get(\"CODEX_BRIDGE_IO_TIMEOUT\", \"30\"))\n\ndef log(msg: str) -> None:\n try:\n with open(LOG, \"a\") as f:\n f.write(time.strftime(\"%Y-%m-%dT%H:%M:%SZ\", time.gmtime()) + \" \" + msg + \"\\n\")\n except OSError:\n pass\n\ndef ws_connect(path: str, timeout: float = CONNECT_TIMEOUT_S):\n \"\"\"Connect + HTTP upgrade under one absolute monotonic deadline.\n\n Returns (socket, trailing) where trailing holds bytes coalesced after the\n upgrade headers (TCP may deliver the 101 and the first WS frame together;\n discarding them would lose the first message). Requires HTTP/1.1 101 plus\n a matching Sec-WebSocket-Accept so a 200 (or any non-101) fails fast even\n when the accept hash happens to be valid.\n \"\"\"\n deadline = time.monotonic() + timeout\n s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)\n s.settimeout(timeout)\n try:\n s.connect(path)\n key = base64.b64encode(os.urandom(16)).decode()\n expected = base64.b64encode(hashlib.sha1((key + WS_GUID).encode()).digest()).decode()\n req = (\n f\"GET /rpc HTTP/1.1\\r\\nHost: localhost\\r\\nUpgrade: websocket\\r\\n\"\n f\"Connection: Upgrade\\r\\nSec-WebSocket-Key: {key}\\r\\nSec-WebSocket-Version: 13\\r\\n\\r\\n\"\n ).encode()\n s.settimeout(max(0.0, deadline - time.monotonic()))\n s.sendall(req)\n buf = b\"\"\n while True:\n if b\"\\r\\n\\r\\n\" in buf:\n break\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"WebSocket upgrade timed out\")\n if len(buf) > 65536:\n raise ConnectionError(\"WS upgrade headers too large\")\n s.settimeout(remaining)\n chunk = s.recv(4096)\n if not chunk:\n raise ConnectionError(\"daemon closed during WS upgrade\")\n buf += chunk\n head, _, trailing = buf.partition(b\"\\r\\n\\r\\n\")\n lines = head.split(b\"\\r\\n\")\n if not lines or lines[0].split(b\" \", 2)[:2] != [b\"HTTP/1.1\", b\"101\"]:\n raise ConnectionError(f\"WS upgrade failed: {head[:200]!r}\")\n accept = None\n for line in lines[1:]:\n name, sep, value = line.partition(b\":\")\n if sep and name.strip().lower() == b\"sec-websocket-accept\":\n accept = value.strip().decode(\"latin-1\")\n if accept != expected:\n raise ConnectionError(\"WS upgrade accept mismatch\")\n except Exception:\n try:\n s.close()\n except OSError:\n pass\n raise\n # Keep one socket mode across reader and writer threads. Reads wait\n # indefinitely in select; sends use their own absolute deadline.\n s.setblocking(False)\n return s, trailing\n\ndef mask_send(sock: socket.socket, payload: bytes, opcode: int = 1, deadline=None) -> None:\n mask = os.urandom(4)\n ln = len(payload)\n if ln < 126:\n hdr = bytes([0x80 | opcode, 0x80 | ln])\n elif ln < 65536:\n hdr = bytes([0x80 | opcode, 0x80 | 126]) + struct.pack(\"!H\", ln)\n else:\n hdr = bytes([0x80 | opcode, 0x80 | 127]) + struct.pack(\"!Q\", ln)\n view = memoryview(hdr + mask + bytes(b ^ mask[i % 4] for i, b in enumerate(payload)))\n while view:\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"socket write timed out\")\n _, writable, _ = select.select([], [sock], [], remaining)\n if not writable:\n raise TimeoutError(\"socket write timed out\")\n try:\n n = sock.send(view)\n except BlockingIOError:\n continue\n if not n:\n raise ConnectionError(\"socket closed during write\")\n view = view[n:]\n\ndef locked_send(sock: socket.socket, lock: threading.Lock, payload: bytes, opcode: int = 1, timeout: float = CONNECT_TIMEOUT_S) -> None:\n \"\"\"Serialized send sharing one monotonic budget across lock wait + send.\n\n A non-reading peer must never wedge pong writes or timeout cleanup: on\n expiry raise TimeoutError so the caller closes and exits without hanging.\n \"\"\"\n if timeout is None:\n timeout = CONNECT_TIMEOUT_S\n deadline = time.monotonic() + timeout\n if timeout <= 0:\n raise TimeoutError(\"send budget expired\")\n if not lock.acquire(timeout=timeout):\n raise TimeoutError(\"send lock timed out\")\n try:\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"send budget expired\")\n mask_send(sock, payload, opcode=opcode, deadline=deadline)\n finally:\n lock.release()\n\ndef _buffered_reader(sock: socket.socket, initial: bytes = b\"\"):\n buf = bytearray(initial)\n def recv(n):\n while len(buf) < n:\n select.select([sock], [], [])\n try:\n chunk = sock.recv(n - len(buf))\n except BlockingIOError:\n continue\n if not chunk:\n raise ConnectionError(\"eof\")\n buf.extend(chunk)\n out = bytes(buf[:n])\n del buf[:n]\n return out\n return recv\n\ndef read_frame(recv):\n hdr = recv(2)\n fin = bool(hdr[0] & 0x80)\n opcode = hdr[0] & 0x0F\n masked = bool(hdr[1] & 0x80)\n ln = hdr[1] & 0x7F\n if ln == 126:\n ln = struct.unpack(\"!H\", recv(2))[0]\n elif ln == 127:\n ln = struct.unpack(\"!Q\", recv(8))[0]\n mask = recv(4) if masked else b\"\"\n payload = recv(ln)\n if masked:\n payload = bytes(b ^ mask[i % 4] for i, b in enumerate(payload))\n return fin, opcode, payload\n\ndef read_message(recv, send_pong):\n \"\"\"Next complete data message, assembling continuation frames.\n\n Control frames are handled inline (pong answered, close reported) so a\n fragmented message split around a ping still reassembles.\n \"\"\"\n opcode = None\n parts = []\n while True:\n fin, op, payload = read_frame(recv)\n if op == 0x8:\n return (\"close\", None, b\"\")\n if op == 0x9:\n send_pong(payload)\n continue\n if op == 0xA:\n continue\n if op in (0x1, 0x2):\n if opcode is not None:\n raise ConnectionError(\"data frame inside fragmented message\")\n if fin:\n return (\"data\", op, payload)\n opcode = op\n parts.append(payload)\n continue\n if op == 0x0:\n if opcode is None:\n raise ConnectionError(\"stray continuation frame\")\n parts.append(payload)\n if fin:\n return (\"data\", opcode, b\"\".join(parts))\n continue\n raise ConnectionError(f\"unsupported opcode {op}\")\n\ndef _is_daemon_response(payload: bytes, expect_id=None, known_ids=None) -> bool:\n \"\"\"True only for the matching JSON-RPC response/error, never a notification.\n\n The first-RPC timer must survive unrelated daemon notifications and foreign\n responses: only the response/error whose id matches the client's initialize\n (or, before it is known, any id the client already sent) proves the first\n RPC got an answer. Timeouts clear it via the deadline, never via data.\n \"\"\"\n try:\n text = payload.decode(\"utf-8\").strip()\n msg = json.loads(text)\n except Exception:\n return False\n if not isinstance(msg, dict):\n return False\n rid = msg.get(\"id\")\n if rid is None or \"method\" in msg or (\"result\" not in msg and \"error\" not in msg):\n return False\n if expect_id is not None:\n return rid == expect_id\n if known_ids:\n return rid in known_ids\n return False\n\ndef _bounded_stdout_write(data: bytes, timeout: float = IO_TIMEOUT_S) -> None:\n \"\"\"Write one daemon frame to stdout without hanging on a non-reading peer.\n\n Waits for writability under the mid-session write budget and writes via\n os.write in a loop, so a Desktop that stopped reading cannot hang the\n bridge and then exit 1 on dirty stdout.\n \"\"\"\n if timeout is None:\n timeout = IO_TIMEOUT_S\n if timeout <= 0:\n raise TimeoutError(\"stdout budget expired\")\n fd = sys.stdout.fileno()\n deadline = time.monotonic() + timeout\n was_blocking = os.get_blocking(fd)\n os.set_blocking(fd, False)\n try:\n view = memoryview(data)\n while view:\n remaining = deadline - time.monotonic()\n if remaining <= 0:\n raise TimeoutError(\"stdout write timed out\")\n _, writable, _ = select.select([], [fd], [], remaining)\n if not writable:\n raise TimeoutError(\"stdout write timed out\")\n try:\n n = os.write(fd, view)\n except BlockingIOError:\n continue\n view = view[n:]\n finally:\n os.set_blocking(fd, was_blocking)\n\ndef stdout_writer(sock: socket.socket, recv, send_pong, done: threading.Event, first_msg: threading.Event, output_forwarded: threading.Event, client_ids=None, init_state=None) -> None:\n try:\n while True:\n kind, opcode, payload = read_message(recv, send_pong)\n if kind == \"close\":\n log(\"daemon WS close\")\n return\n if opcode == 0x1:\n # Desktop StdioConnection expects newline-delimited JSON text\n if not payload.endswith(b\"\\n\"):\n payload += b\"\\n\"\n # Commit BEFORE writing: once a daemon byte is about to hit\n # stdout, the session is dirty and the fallback must never run,\n # even if the write itself blocks or fails.\n output_forwarded.set()\n _bounded_stdout_write(payload)\n if opcode == 0x1:\n expect = (init_state or {}).get(\"initialize_id\") if isinstance(init_state, dict) else None\n if _is_daemon_response(payload, expect, client_ids or ()):\n first_msg.set()\n except Exception as e:\n log(f\"stdout_writer exit: {e}\")\n finally:\n done.set()\n\ndef main() -> int:\n log(f\"start sock={SOCK}\")\n try:\n sock, pending = ws_connect(SOCK)\n except Exception as e:\n log(f\"connect failed: {e}\")\n sys.stderr.write(f\"codex-stdio-to-daemon-ws: {e}\\n\")\n return EXIT_PRE_STDIO\n log(\"connected\")\n connected_at = time.monotonic()\n first_deadline = connected_at + FIRST_MESSAGE_TIMEOUT_S\n send_lock = threading.Lock()\n done = threading.Event()\n first_msg = threading.Event()\n output_forwarded = threading.Event()\n client_ids = set()\n init_state = {\"initialize_id\": None}\n\n def _send_timeout() -> float:\n # Before the first matching daemon response the remaining first-RPC\n # budget bounds every send; after it each send gets the mid-session\n # write budget. Idle reads are never timed out.\n if not first_msg.is_set():\n return max(0.0, first_deadline - time.monotonic())\n return IO_TIMEOUT_S\n\n def _cleanup_timeout() -> float:\n # Timeout cleanup stays within the expired session budget: once the\n # first-RPC deadline has passed, close directly instead of opening\n # fresh windows that let a short budget take several timeout periods.\n if not first_msg.is_set():\n return max(0.0, first_deadline - time.monotonic())\n return CONNECT_TIMEOUT_S\n\n def send_pong(payload: bytes) -> None:\n locked_send(sock, send_lock, payload, opcode=0xA, timeout=_send_timeout())\n recv = _buffered_reader(sock, pending)\n t = threading.Thread(target=stdout_writer, args=(sock, recv, send_pong, done, first_msg, output_forwarded, client_ids, init_state), daemon=True)\n t.start()\n # Byte-precise commit point: the fallback may run only while zero stdin\n # bytes have left the pipe and no daemon payload was committed to stdout.\n # Reads use os.read into a userspace line buffer (buffered readline could\n # readahead past the commit point), and every byte read counts -- even a\n # blank line. first_msg tracks the matching daemon response/error, never\n # a notification.\n stdin_bytes = 0\n pending_in = b\"\"\n clean_eof = False\n try:\n fd = sys.stdin.fileno()\n while not done.is_set():\n if not first_msg.is_set() and time.monotonic() >= first_deadline:\n log(\"first daemon message timed out\")\n break\n ready, _, _ = select.select([fd], [], [], 0.5)\n if not ready:\n continue\n chunk = os.read(fd, 65536)\n if not chunk:\n clean_eof = True\n break\n stdin_bytes += len(chunk)\n pending_in += chunk\n while b\"\\n\" in pending_in:\n line, _, pending_in = pending_in.partition(b\"\\n\")\n line = line.strip()\n if not line:\n continue\n try:\n try:\n obj = json.loads(line.decode(\"utf-8\"))\n except Exception:\n obj = None\n if isinstance(obj, dict) and obj.get(\"id\") is not None:\n client_ids.add(obj.get(\"id\"))\n if obj.get(\"method\") == \"initialize\" and init_state[\"initialize_id\"] is None:\n init_state[\"initialize_id\"] = obj.get(\"id\")\n except Exception:\n pass\n try:\n locked_send(sock, send_lock, line, timeout=_send_timeout())\n except TimeoutError as e:\n log(f\"stdin forward timed out: {e}\")\n break\n except Exception as e:\n log(f\"stdin forward failed: {e}\")\n break\n else:\n continue\n break\n except Exception as e:\n log(f\"stdin loop exit: {e}\")\n # Flush any buffered EOF-tail data BEFORE the WS Close: sending Close\n # first would let the daemon discard the tail.\n if clean_eof:\n tail = pending_in.strip()\n if tail and not done.is_set():\n try:\n locked_send(sock, send_lock, tail, timeout=_cleanup_timeout())\n except Exception as e:\n log(f\"eof flush failed: {e}\")\n try:\n locked_send(sock, send_lock, b\"\", opcode=0x8, timeout=_cleanup_timeout())\n except Exception as e:\n log(f\"close send failed: {e}\")\n try:\n try:\n sock.shutdown(socket.SHUT_RDWR)\n except OSError:\n pass\n sock.close()\n except OSError:\n pass\n # Bound termination: wake the reader so no thread is left hung on recv.\n t.join(timeout=0.5)\n # A reader still exiting could commit output after this check. In that\n # case pristine stdio cannot be proven, so never permit fallback.\n if t.is_alive():\n output_forwarded.set()\n if clean_eof and (stdin_bytes or output_forwarded.is_set()):\n log(\"exit\")\n return EXIT_SERVED\n # Reader died, first RPC timed out, post-upgrade I/O timed out, or stdin\n # broke: fail open only while stdio is still pristine -- no stdin byte\n # ever consumed AND no daemon payload ever committed to stdout. Committed\n # output forces a mid-session exit so Desktop reconnects instead of\n # falling back to stock Codex on a corrupted stream.\n rc = EXIT_MID_SESSION if (stdin_bytes or output_forwarded.is_set()) else EXIT_PRE_STDIO\n log(f\"exit rc={rc}\")\n return rc\n\nif __name__ == \"__main__\":\n raise SystemExit(main())\n"; diff --git a/src/core/desktop-shim.js b/src/core/desktop-shim.js index 8136b31..ab5831a 100644 --- a/src/core/desktop-shim.js +++ b/src/core/desktop-shim.js @@ -62,20 +62,22 @@ export function defaultPaths({ env = process.env, home = userHomeDir(env) } = {} /** * Pure passthrough decision, mirroring the wrapper's argv scan. True only for * a bare `app-server` spawn: Desktop's private-stdio case. `daemon`, `proxy`, - * and schema generations always exec the real Codex. + * schema generations, and one-shot startup/help/version requests exec real Codex. */ export function shouldBridge(argv) { let appServer = false; let daemon = false; let proxy = false; let generate = false; + let oneShot = false; for (const arg of argv ?? []) { + if (["--print-startup-config", "--startup-sign-out", "help", "--help", "-h", "--version", "-V"].includes(arg)) oneShot = true; if (arg === "app-server") appServer = true; else if (arg === "daemon") daemon = true; else if (arg === "proxy") proxy = true; else if (arg === "generate-ts" || arg === "generate-json-schema") generate = true; } - return appServer && !daemon && !proxy && !generate; + return appServer && !daemon && !proxy && !generate && !oneShot; } export function renderWrapperScript({ realPath, bridgePath, wrapperLogPath, codexHome, bridgeLogPath }) { @@ -83,6 +85,7 @@ export function renderWrapperScript({ realPath, bridgePath, wrapperLogPath, code # Installed by \`gbot codex desktop-shim install\`. Rewrites ChatGPT Desktop's # \`codex ... app-server\` spawn into a stdio<->WebSocket bridge onto the managed # daemon's control socket. Reversible: \`gbot codex desktop-shim uninstall\`. +# Startup configuration, sign-out, help and version requests use the real CLI. # The daemon is kept up by the LaunchAgent login script, not here: this hot path # never starts the daemon itself, so a wedged daemon lock cannot hang # Desktop. Fail-open runs ONLY while stdio is still pristine (no stdin @@ -115,9 +118,10 @@ if [[ ! -x "$REAL" ]]; then fi fi -has_app_server=0; has_daemon=0; has_proxy=0; has_generate=0 +has_app_server=0; has_daemon=0; has_proxy=0; has_generate=0; has_one_shot=0 for a in "$@"; do case "$a" in + --print-startup-config|--startup-sign-out|help|--help|-h|--version|-V) has_one_shot=1 ;; app-server) has_app_server=1 ;; daemon) has_daemon=1 ;; proxy) has_proxy=1 ;; @@ -131,7 +135,7 @@ preflight() { python3 -c 'import os,socket,sys; s=socket.socket(socket.AF_UNIX,socket.SOCK_STREAM); s.settimeout(float(os.environ.get("CODEX_DESKTOP_PREFLIGHT_TIMEOUT","3"))); s.connect(sys.argv[1]); s.close()' "$SOCK" 2>/dev/null } -if [[ "$has_app_server" -eq 1 && "$has_daemon" -eq 0 && "$has_proxy" -eq 0 && "$has_generate" -eq 0 ]]; then +if [[ "$has_app_server" -eq 1 && "$has_daemon" -eq 0 && "$has_proxy" -eq 0 && "$has_generate" -eq 0 && "$has_one_shot" -eq 0 ]]; then if [[ -f "$BRIDGE" ]] && command -v python3 >/dev/null 2>&1; then if preflight; then echo "$(ts) rewrite -> stdio-ws bridge" >>"$LOG" 2>/dev/null || true diff --git a/test/desktop-shim-startup.test.js b/test/desktop-shim-startup.test.js new file mode 100644 index 0000000..7181b41 --- /dev/null +++ b/test/desktop-shim-startup.test.js @@ -0,0 +1,244 @@ +import assert from "node:assert/strict"; +import { spawnSync } from "node:child_process"; +import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { createServer } from "node:net"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import test from "node:test"; + +import { renderWrapperScript, shouldBridge } from "../src/core/desktop-shim.js"; + +const canRunShellBridge = (() => { + if (process.platform === "win32") return false; + try { + const bash = spawnSync("bash", ["--version"], { encoding: "utf8" }); + const python = spawnSync("python3", ["--version"], { encoding: "utf8" }); + return bash.status === 0 && python.status === 0; + } catch { + return false; + } +})(); + +const CLIENT_INFO = JSON.stringify({ + name: "codex_desktop", + title: "Codex Desktop", + version: "26.1027.61615", +}); + +const STARTUP_CONFIG = JSON.stringify({ + appServerVersion: "0.166.0", + configRequirements: { requirements: {} }, +}); + +const ONE_SHOT_CASES = [ + { + label: "startup config", + argv: ["-c", "features.code_mode_host=true", "app-server", "--analytics-default-enabled", "--print-startup-config", "--startup-client-info", CLIENT_INFO], + exit: 0, + stdout: STARTUP_CONFIG, + }, + { + label: "startup config combined with sign-out", + argv: [ + "-c", + "features.code_mode_host=true", + "app-server", + "--analytics-default-enabled", + "--print-startup-config", + "--startup-client-info", + CLIENT_INFO, + "--startup-sign-out", + ], + exit: 0, + stdout: STARTUP_CONFIG, + }, + { + label: "startup config with equals-form client identity", + argv: ["app-server", "--print-startup-config", `--startup-client-info=${CLIENT_INFO}`], + exit: 0, + stdout: STARTUP_CONFIG, + }, + { + label: "startup config child failure is not swallowed", + argv: ["app-server", "--analytics-default-enabled", "--print-startup-config", "--startup-client-info", CLIENT_INFO], + exit: 7, + stdout: "", + stderr: "real codex failed\n", + }, + ...["--startup-sign-out", "--help", "-h", "--version", "-V"].map((flag) => ({ + label: flag, + argv: ["app-server", flag], + exit: 0, + stdout: `real CLI ${flag}`, + })), + ...[["help", "app-server"], ["app-server", "help"]].map((argv) => ({ + label: argv.join(" "), + argv, + exit: 0, + stdout: "real CLI help", + })), +]; + +// Records argv, then emits exactly what the case asks for and exits with its +// code, so the wrapper's passthrough can be observed byte for byte. +const FAKE_REAL_SCRIPT = `#!/bin/bash +{ + for a in "$@"; do printf '%s\\n' "$a"; done +} > "$FAKE_REAL_ARGS" +printf '%s' "\${FAKE_REAL_STDOUT:-}" +printf '%s' "\${FAKE_REAL_STDERR:-}" >&2 +exit "\${FAKE_REAL_EXIT:-0}" +`; + +function writeExecutable(path, content) { + writeFileSync(path, content, { mode: 0o755 }); + chmodSync(path, 0o755); +} + +/** Generated wrapper over a fake real CLI and a fake bridge that reports entry. */ +function stageFixture(dir) { + const realPath = join(dir, "real-codex"); + const bridgePath = join(dir, "codex-stdio-to-daemon-ws.py"); + const bridgeMarker = join(dir, "bridge-ran.txt"); + const wrapperPath = join(dir, "wrapper.sh"); + writeExecutable(realPath, FAKE_REAL_SCRIPT); + writeExecutable( + bridgePath, + `#!/usr/bin/env python3 +import pathlib +pathlib.Path(${JSON.stringify(bridgeMarker)}).write_text("yes") +print("BRIDGE-RAN") +`, + ); + writeExecutable( + wrapperPath, + renderWrapperScript({ + bridgeLogPath: join(dir, "bridge.log"), + bridgePath, + codexHome: dir, + realPath, + wrapperLogPath: join(dir, "wrapper.log"), + }), + ); + return { bridgeMarker, wrapperPath }; +} + +/** Unix listener that only needs to accept the preflight connect and close. */ +async function silentListener(socketPath) { + rmSync(socketPath, { force: true }); + const sockets = new Set(); + const server = createServer((socket) => { + sockets.add(socket); + socket.on("error", () => {}); + socket.on("close", () => sockets.delete(socket)); + }); + await new Promise((resolve, reject) => { + server.once("error", reject); + server.listen(socketPath, resolve); + }); + return { + socketPath, + close: async () => { + for (const socket of sockets) socket.destroy(); + await new Promise((resolve) => server.close(resolve)); + }, + }; +} + +/** Desktop runs the one-shot child with ignored stdin and piped output. */ +function runWrapper(wrapperPath, argv, env) { + return spawnSync("bash", [wrapperPath, ...argv], { + encoding: "utf8", + env, + stdio: ["ignore", "pipe", "pipe"], + timeout: 60000, + }); +} + +function readRealArgs(path) { + const text = readFileSync(path, "utf8"); + return (text.endsWith("\n") ? text.slice(0, -1) : text).split("\n"); +} + +function wrapperLog(dir) { + const path = join(dir, "wrapper.log"); + return existsSync(path) ? readFileSync(path, "utf8") : ""; +} + +test("generated wrapper forwards Desktop one-shot startup commands to the real CLI", { + skip: !canRunShellBridge && "needs bash + python3", +}, async (t) => { + const root = mkdtempSync(join(tmpdir(), "gbot-shim-startup-")); + const daemon = await silentListener(join(root, "daemon.sock")); + t.after(async () => { + await daemon.close(); + rmSync(root, { recursive: true, force: true }); + }); + + for (const [index, tc] of ONE_SHOT_CASES.entries()) { + await t.test(tc.label, () => { + assert.equal(shouldBridge(tc.argv), false); + const dir = join(root, `case-${index}`); + mkdirSync(dir); + const { bridgeMarker, wrapperPath } = stageFixture(dir); + const argsPath = join(dir, "real-args.txt"); + const result = runWrapper(wrapperPath, tc.argv, { + ...process.env, + CODEX_APP_SERVER_SOCK: daemon.socketPath, + CODEX_HOME: dir, + FAKE_REAL_ARGS: argsPath, + FAKE_REAL_EXIT: String(tc.exit), + FAKE_REAL_STDERR: tc.stderr ?? "", + FAKE_REAL_STDOUT: tc.stdout ?? "", + }); + + assert.ok( + existsSync(argsPath), + `real CLI was never invoked (status=${result.status}, stdout=${JSON.stringify(result.stdout)}); ` + + `wrapper log: ${wrapperLog(dir)}`, + ); + assert.deepEqual(readRealArgs(argsPath), tc.argv, "real CLI must receive Desktop's argv unchanged"); + assert.equal(result.stdout, tc.stdout ?? ""); + assert.equal(result.stderr, tc.stderr ?? ""); + assert.equal(result.status, tc.exit); + assert.equal(existsSync(bridgeMarker), false, "one-shot command must not enter the interactive bridge"); + }); + } +}); + +test("generated wrapper still bridges ordinary interactive app-server sessions", { + skip: !canRunShellBridge && "needs bash + python3", +}, async (t) => { + for (const argv of [ + ["app-server", "--analytics-default-enabled"], + ["app-server", "--listen", "stdio://"], + ["-c", "features.code_mode_host=true", "app-server", "--analytics-default-enabled"], + ]) { + await t.test(argv.join(" "), async (t) => { + assert.equal(shouldBridge(argv), true); + const dir = mkdtempSync(join(tmpdir(), "gbot-shim-interactive-")); + const daemon = await silentListener(join(dir, "daemon.sock")); + t.after(async () => { + await daemon.close(); + rmSync(dir, { recursive: true, force: true }); + }); + const { bridgeMarker, wrapperPath } = stageFixture(dir); + const argsPath = join(dir, "real-args.txt"); + + const result = runWrapper(wrapperPath, argv, { + ...process.env, + CODEX_APP_SERVER_SOCK: daemon.socketPath, + CODEX_HOME: dir, + FAKE_REAL_ARGS: argsPath, + FAKE_REAL_EXIT: "0", + FAKE_REAL_STDERR: "", + FAKE_REAL_STDOUT: "", + }); + + assert.equal(result.status, 0, result.stderr); + assert.match(result.stdout, /BRIDGE-RAN/); + assert.ok(existsSync(bridgeMarker), "interactive app-server must still enter the bridge"); + assert.equal(existsSync(argsPath), false, "interactive app-server must not exec the real CLI when the daemon is reachable"); + }); + } +}); diff --git a/test/desktop-shim.test.js b/test/desktop-shim.test.js index 4d3ec83..1e301fa 100644 --- a/test/desktop-shim.test.js +++ b/test/desktop-shim.test.js @@ -34,6 +34,20 @@ test("shouldBridge only rewrites bare app-server spawns", () => { assert.equal(shouldBridge(["app-server", "generate-ts"]), false); }); +test("shouldBridge preserves Desktop one-shot startup and CLI help/version requests", () => { + const clientInfo = JSON.stringify({ name: "ChatGPT", version: "1.0" }); + const interactive = ["app-server", "--analytics-default-enabled", "--startup-client-info", clientInfo]; + assert.equal(shouldBridge(interactive), true); + for (const flag of ["--print-startup-config", "--startup-sign-out", "--help", "-h", "--version", "-V"]) { + assert.equal(shouldBridge([...interactive, flag]), false, flag); + assert.equal(shouldBridge([flag, ...interactive]), false, `leading ${flag}`); + } + assert.equal(shouldBridge([...interactive, "--print-startup-config", "--startup-sign-out"]), false); + assert.equal(shouldBridge(["help", "app-server"]), false); + assert.equal(shouldBridge(["app-server", "help"]), false); + assert.equal(shouldBridge(["app-server", "--startup-client-info", JSON.stringify({ name: "--help" })]), true); +}); + test("default paths live under the Codex home, never the package checkout", () => { const paths = defaultPaths({ env: { CODEX_HOME: "/tmp/shim-home", HOME: "/tmp/shim-user" }, home: "/tmp/shim-user" }); assert.equal(paths.codexHome, "/tmp/shim-home"); @@ -551,6 +565,51 @@ const writeBridge = (dir) => { return bridgePath; }; +test("bridge permits fallback only when EOF leaves both streams pristine", { + skip: !canRunShellBridge && "needs bash + python3", +}, async (t) => { + for (const [input, expectedStatus] of [["", 1], ["{\"id\":1}\n", 0]]) { + await t.test(input ? "consumed input remains served" : "empty EOF permits fallback", async () => { + const dir = mkdtempSync(join(tmpdir(), "gbot-shim-eof-")); + const daemon = await fakeWsDaemon(dir, "eof.sock", { quiet: true }); + try { + const out = await runBridge(writeBridge(dir), dir, + { CODEX_APP_SERVER_SOCK: daemon.socketPath }, input); + assert.equal(out.status, expectedStatus, out.stderr); + assert.equal(out.stdout, ""); + } finally { + await closeDaemon(daemon); + rmSync(dir, { recursive: true, force: true }); + } + }); + } +}); + +test("wrapper falls back for an unrecognized one-shot after pristine bridge EOF", { + skip: !canRunShellBridge && "needs bash + python3", +}, async () => { + const dir = mkdtempSync(join(tmpdir(), "gbot-shim-future-")); + const daemon = await fakeWsDaemon(dir, "future.sock", { quiet: true }); + try { + const { wrapperPath } = stageWrapper(dir, { bridgePath: writeBridge(dir) }); + const child = spawn("bash", [wrapperPath, "app-server", "--print-future-config"], { + env: { ...process.env, CODEX_APP_SERVER_SOCK: daemon.socketPath }, + timeout: 10000, + }); + let stdout = ""; + let stderr = ""; + child.stdout.on("data", (chunk) => { stdout += chunk; }); + child.stderr.on("data", (chunk) => { stderr += chunk; }); + child.stdin.end(); + const [status] = await once(child, "close"); + assert.equal(status, 0, stderr); + assert.equal(stdout.trim(), "FAKE-REAL argv: app-server --print-future-config"); + } finally { + await closeDaemon(daemon); + rmSync(dir, { recursive: true, force: true }); + } +}); + test("bridge keeps frames coalesced with the upgrade and assembles continuations", { skip: !canRunShellBridge && "needs bash + python3", }, async () => {