Skip to main content
Glama
yueseqaz
by yueseqaz

BastionPilot

CI 中文文档

Bring AI agents to bastion-host web terminals.

In many companies the only way to reach production servers is through a bastion host (JumpServer, Qizhi, Aliyun Bastionhost, ...) that exposes a web terminal in the browser. There is no SSH access, so CLI agents like Claude Code or Codex cannot reach your servers. BastionPilot fixes that: a browser extension hooks the web terminal and acts as the agent's hands and eyes, while a local bridge exposes clean, auditable tools to any AI brain you prefer.

Claude Code / Codex            Side-panel chat
   (MCP stdio)                       │
        │                            │ HTTPS → your AI provider
        ▼                            ▼
┌─────────────────────────────────────────────┐
│            local bridge (daemon)             │
│  MCP server │ built-in agent │ approvals │   │
│              │    loop      │ audit log  │   │
└──────────────┬──────────────────────────────┘
               │ ws://127.0.0.1 + pairing token
               ▼
┌─────────────────────────────────────────────┐
│           browser extension (MV3)            │
│  MAIN world: WebSocket interception (I/O)    │
│  content controller: session + buffers       │
│  service worker: tool routing, approvals     │
└──────────────┬──────────────────────────────┘
               │ the page's own WebSocket
               ▼
   bastion web terminal (JumpServer / any xterm.js product)

How it works

  • The extension is the only owner of terminal I/O. A main-world script injected at document_start patches window.WebSocket, tees every downstream frame (the PTY stream) and can type into the terminal on request. Whatever the bastion product renders — xterm.js on canvas, WebGL, anything — the raw stream is captured before it is drawn.

  • Everything else connects to it. Claude Code / Codex talk to the daemon's MCP server; the side-panel chat uses the daemon's built-in agent loop with your own OpenAI-compatible provider. Both brains funnel through the same tool executor, the same approval policy and the same audit log.

  • Output is made LLM-ready. PTY bytes are decoded (UTF-8, chunk-safe), ANSI/OSC escape sequences are stripped, \r/\b overwrite semantics are applied (progress bars collapse into their final line), and oversized output is written to a local file whose path is returned to the agent — Claude Code can then read the full file with its own tools.

  • Command completion is detected reliably. After typing a command, BastionPilot appends an echo __BP_<nonce>_$?__ sentinel. The literal $? in the echoed input can never match the result pattern, so the sentinel resolves exactly once, carrying the exit code. Quiet detection and timeouts back it up for interactive situations.

  • Products are abstracted into thin profiles. WebSocket transport is universal; framing (raw / JSON / base64) and DOM hints live in declarative site profiles. JumpServer ships built in; anything xterm.js-based works with the generic profile.

Related MCP server: browserpilot-mcp

What you get

Brain

How to connect

Claude Code / Codex / any MCP client

bastion-pilot mcp (stdio) — recommended

Built-in side-panel chat

any OpenAI-compatible endpoint (cloud or self-hosted/intranet)

Tools exposed to agents: list_sessions, send_command, read_output, wait_output, send_key, get_screen, upload_file, download_file — see docs/tools.md for the full contract. Every tool result is also appended to per-session transcripts for post-incident review (bastion-pilot transcripts).

Safety rails:

  • Approval modes (auto / confirm / readonly + allowlist), enforced on the bridge for every brain. Default is confirm: each mutating command waits for your click (side panel or OS notification).

  • Local audit log: one JSONL line per tool call under ~/.bastion-pilot/audit/.

  • Read-only tool classification: list_sessions, read_output, wait_output, get_screen never require approval.

  • Opt-in site access: the extension only runs on origins you explicitly grant in its options.

  • The bastion's own session recording keeps recording — BastionPilot types like a human, so your compliance story is unchanged.

Quickstart

1. Bridge (local daemon + MCP server)

Requires Node.js ≥ 20.

git clone https://github.com/yueseqaz/bastion-pilot.git
cd bastion-pilot
pnpm install && pnpm build

# create config + pairing token, print setup instructions
node packages/bridge/dist/cli.js init

# start the daemon (keep it running; the MCP server can also auto-start it)
node packages/bridge/dist/cli.js daemon

# check connectivity at any time
node packages/bridge/dist/cli.js doctor

init creates ~/.bastion-pilot/config.json (chmod 600) containing the pairing token, and prints the token plus copy-paste snippets for Claude Code / Codex.

2. Browser extension

Chrome / Edge (Chromium ≥ 116):

  1. Build it once: pnpm --filter @bastion-pilot/extension build (or grab the zip from GitHub Releases / CI artifacts and unzip it).

  2. Open chrome://extensions, enable Developer mode, click Load unpacked, select packages/extension/dist.

  3. Open the BastionPilot side panel (click the toolbar icon) → ⚙ Settings → paste the daemon URL and pairing token → Save → Test connection.

  4. Under Bastion sites, grant access to your bastion origin, e.g. https://bastion.example.com.

3. Wire up a brain

Claude Code

claude mcp add bastion-pilot -- node /absolute/path/to/bastion-pilot/packages/bridge/dist/cli.js mcp

Codex CLI — add to ~/.codex/config.toml:

[mcp_servers.bastion-pilot]
command = "node"
args = ["/absolute/path/to/bastion-pilot/packages/bridge/dist/cli.js", "mcp"]

Then, in your agent, ask something like: "list the bastion sessions, and on web-01 check disk usage". The agent will call list_sessions, then send_command df -h, read the output, and answer — subject to your approval mode.

Built-in side-panel chat (optional) — open the extension's Settings → AI providers section, add an API Key service, enter only its endpoint URL and API key, then click Fetch models and choose a model. The same settings can also be edited directly in ~/.bastion-pilot/config.json:

{
  "providers": [
    {
      "id": "default",
      "baseUrl": "https://api.openai.com/v1",
      "apiKey": "sk-...",
      "model": "gpt-4o"
    }
  ],
  "activeProviderId": "default"
}

The side panel's Mode selector is mutually exclusive: API Key calls the configured provider directly, while Local Agent lists connected Claude Code / Codex MCP conversations so you can target one conversation. The selected agent reads the request with get_shared_context and reports back with append_shared_context; set BASTION_PILOT_CONVERSATION_ID (or CODEX_THREAD_ID) in the MCP process environment for a human-readable conversation identity.

Self-hosted / intranet gateways (vLLM, Ollama's OpenAI adapter, corporate LLM gateways) work with the same shape — the terminal output never has to leave your network if you don't want it to. Side-panel history survives restarts: reopening the panel restores the latest conversation.

Configuration

~/.bastion-pilot/config.json (created by bastion-pilot init):

Key

Default

Meaning

server.port

8765

daemon listen port (always bound to 127.0.0.1)

security.token

generated

pairing token for the extension & MCP server

approval.mode

confirm

auto / confirm / readonly

approval.requireUserApproval

true

every command, keystroke and file operation needs an explicit panel approval

approval.confirmTimeoutMs

120000

unanswered approvals are denied after this

approval.readonlyAllowlist

[]

regex list; in readonly mode matching commands are allowed

providers[]

[]

OpenAI-compatible providers for the built-in chat

activeProviderId

null

provider used by the side panel

agent.maxIterations

30

loop cap for the built-in agent

agent.systemPrompt

built-in

replace the default safety-first prompt

output.maxInlineChars

6000

larger outputs are truncated + saved to a file

transcripts.enabled

true

per-session terminal transcripts under ~/.bastion-pilot/transcripts/

Extension-side settings (options page): daemon URL, pairing token, AI providers, granted origins and the site profile per origin (generic-xterm / jumpserver). AI provider settings are written by the authenticated local daemon and are not stored in browser sync storage.

Troubleshooting

Symptom

Fix

doctor says extension not connected

Is the daemon running? Token pasted in the options page? Click the toolbar icon to wake the service worker.

Tool calls fail with "no terminal session"

Open the bastion terminal tab after granting the site (or reload it). Check the origin was granted in options.

Garbled output

Your product frames the PTY as JSON/base64 — duplicate a profile and set framer.type, then select it in the options page.

Agent types but nothing happens

Some products intercept keystrokes client-side; try inputMode: "dom" in a custom profile.

Long commands "time out"

They don't fail — pass a larger waitMs, or let the agent use read_output/wait_output with the returned cursor.

Approval never appears

The daemon sends it to the extension; check the side panel and OS notifications, and confirm only one browser profile is paired.

Development

pnpm install
pnpm all        # build + typecheck + test + lint
pnpm test       # 123 tests across protocol / bridge / extension
pnpm --filter @bastion-pilot/extension zip   # release zip for the extension
node scripts/gen-icons.mjs                   # regenerate icons

Monorepo layout:

packages/protocol   shared wire protocol, tool contract, terminal text renderer
packages/bridge     daemon, MCP server, built-in agent loop, approvals, audit
packages/extension  MV3 extension (interceptor, controller, worker, panel, options)
profiles            docs for adapting further bastion products

See docs/tools.md for the agent-facing tool contract and profiles/README.md for adding support for new bastion products.

Security notes

See SECURITY.md. Short version: everything local is bound to 127.0.0.1 and token-authenticated; config/audit/output files are chmod 600; approval policy is enforced centrally; the bastion's own audit trail stays fully intact. You are responsible for complying with your company's policies — including whether terminal output may be sent to a cloud LLM.

Roadmap

  • File upload/download over the terminal (upload_file / download_file, base64 chunks)

  • iframe-based terminals (one session per terminal frame)

  • More site profiles — Teleport, Qizhi, Aliyun Bastionhost (experimental, field-test welcome)

  • Session transcripts replay for post-incident review (bastion-pilot transcripts)

  • npm publish-ready bridge package (pnpm --filter bastion-pilot publish)

  • Published npm package + npx bastion-pilot mcp (needs the maintainer's npm account)

  • Vision fallback for non-xterm terminals (Guacamole-style canvas streaming)

  • Multiple terminals inside a single browser frame

License

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to drive a real, already logged-in browser from the CLI or over MCP, with enforceable approval gates for actions and safeguards against prompt injection.
    1,133,569 npm
    Mozilla Public 2.0
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables remote AI agents to control a local, authenticated desktop browser over MCP through an outbound WebSocket bridge and Chrome extension, supporting tab management, navigation, page reading, clicking, typing, scrolling, screenshots, and JavaScript evaluation.
    1
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables any MCP-capable AI client to drive the user's own Chrome or Edge browser through a set of browse_* tools — navigating, clicking, typing, extracting, and managing tabs with their existing logins on their own machine. Runs locally and self-hosted with a live viewport and pause/approve controls so every verified step stays under user watch.
    4
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI coding tools to drive the user's real, already-logged-in Chrome through an MCP stdio interface, exposing 25 tools for navigating, clicking, typing, reading pages, taking screenshots, capturing network traffic, and accessing cookies and storage. The bundled Chrome extension talks to a local bridge instead of an open remote-debugging port, so logged-in sessions and bot-detection-proof human browsing work without extra setup.
    1 npm
    ISC