AI that shares your terminal session.
Not a chatbot that suggests commands. A collaborator that types alongside you in the same PTY.
Demo β’ Features β’ Codex OAuth β’ Agent API β’ Quick Start β’ Why WinkTerm? β’ Architecture β’ Configuration β’ Development β’ Roadmap
GIF β real SSH session: a mistaken command (ipconfig), then # what's wrong; the AI answers in the same PTY and can pre-fill the fix.
Promo β single-column terminal with multiple SSH tabs; Craft orchestrates checks across hosts (list_ssh_connections, terminal_exec, ssh_run).
$ ipconfig
Command 'ipconfig' not found, did you mean: ...
$ # what's wrong
[WinkTerm] `ipconfig` is a Windows command β on Linux use `ip addr` (or `ifconfig`).
$ ip addrβ β AI wrote this. Press Enter to run. Backspace to edit. Ctrl+C to cancel.
This is not a ChatGPT wrapper pasted into a terminal. The AI writes directly into your terminal's input line. You stay in control β press Enter to execute, edit freely, or cancel. It's like SSH-ing into a server with a knowledgeable partner who can reach across the screen and type.
- Shared PTY Session β AI and user operate in the same terminal process. No copy-paste, no "run this command" without context.
- In-Terminal Chat β Type
#followed by your question, right where your shell prompt is. No need to alt-tab. - Sidebar AI Panel β Full conversational interface with multi-conversation tabs, AI-generated titles, and chat/craft mode switching.
- Persistent Chat History β Conversations are saved to
~/.winkterm/chat_history.jsonand restored on page load; survives WebSocket reconnects and backend restarts. - Streaming Resume β Refresh or reconnect mid-response without losing in-flight tokens; active streams are tracked server-side and replayed to new WebSocket clients.
- Streaming Queue & Suggestions β Queue follow-up messages while the AI is responding (interrupt or drop queued items anytime), and get one-click follow-up suggestion chips after each answer.
- External Agent API β An authenticated HTTP interface lets external agents drive your terminal, SSH, and file transfers via an installable skill (see Agent API highlights below).
- Agent Terminals in Your Tabs β Agent-created sessions appear as normal terminal tabs (no separate monitor panel).
GET /api/sessions/streamkeeps the UI in sync; WebSocket disconnect no longer kills PTY sessions β refresh replays buffered output. - Remote Access Auth β Web access is protected by an access key; the local desktop client needs no authentication.
- SSH Remote Connections β Connect to remote servers with built-in file transfer. Editing a connection without re-entering the password keeps the saved credential.
- Settings Export & Secret-safe Saves β Export
config.jsonfrom Settings (GET /api/settings/export). Blank password or API key fields on save do not wipe stored secrets. - Reliable Terminals β Debounced PTY sizing fixes truncated PowerShell prompts; agent multi-tab creation no longer leaves empty panes; WebSocket reconnect is silent (no disconnect banner that breaks PSReadLine).
- Internationalization β Built-in English / Chinese UI, with language selection on first launch.
- Multi-Model Support β Bring your own LLM (OpenAI, Anthropic, Ollama, any compatible endpoint), or sign in with Codex OAuth and use your ChatGPT / Codex subscription directly.
- Docker & Desktop β Deploy instantly with
docker compose upor package as a standalone desktop app (Windows/macOS).
WinkTerm can talk to OpenAI Codex through an official OAuth flow β no API key, no third-party relay, no codex exec subprocess.
Why use it
| Advantage | What it means for you |
|---|---|
| ChatGPT / Codex subscription | Use models like gpt-5.5 with the account you already pay for β no separate OpenAI API billing. |
| Zero API config | In Settings β API format, choose Codex OAuth, click login, pick a model. No base_url or api_key fields. |
| Official OAuth, local tokens | Browser authorization; tokens land in ~/.codex/auth.json on the machine running WinkTerm. |
| Native WebSocket transport | Sidebar chat and the in-terminal # agent stream over Codex Responses β full tool calls (list_terminals, ssh_run, β¦), multi-turn history, no fallback to shelling out to the CLI. |
| Same human-in-the-loop UX | Craft / chat / ask modes, streaming, queued follow-ups, and tool approval all work the same as with a BYO endpoint. |
| Desktop-friendly | OAuth URL can be copied when the embedded WebView cannot open a browser tab automatically. |
Setup (30 seconds)
- Open Settings β set API format to Codex OAuth.
- Click Login with ChatGPT (or copy the auth URL into your browser).
- Select a Codex model (e.g.
gpt-5.5) and start chatting in the sidebar or with#in any terminal tab.
You can switch back to OpenAI / Anthropic / a custom base URL anytime β Codex OAuth is an option, not a lock-in.
WinkTerm's HTTP Agent API is designed for AI agents (Claude Code, Cursor, etc.) to drive the terminal remotely β not just an afterthought.
| Endpoint | Purpose |
|---|---|
POST /api/agent/terminals/{id}/exec |
Atomic execution: returns stdout + real exit_code + current cwd. Sentinel marker auto-strips command echo and prompt. Supports cwd / env subshell injection (doesn't pollute persistent terminal state). |
POST /api/agent/ssh/{conn_id}/run |
One-shot SSH execution: bundles create β exec β close into one call, saving 3 round-trips. |
POST/GET/PUT/DELETE /api/agent/ssh/connections[/{id}] |
SSH connection management: full CRUD on the stored connection profiles, plus POST /api/agent/ssh/import/electerm. Update leaves masked/omitted secrets unchanged. |
POST /api/agent/terminals/{id}/input |
Named control keys: {"keys": ["ctrl+c"]} instead of stuffing control chars into JSON. data_b64 input bypasses multi-layer quote escape hell. |
GET /api/agent/terminals/{id}/snapshot?pattern=... |
Server-side grep: regex-match within the 256KB rolling buffer. Save bandwidth. |
GET /api/agent/terminals/{id}/stream |
SSE live output: killer feature for long-running commands / tail -f. Resume with since after disconnect. |
GET /api/agent/events/stream |
Operation event feed: every agent action is pushed to a ring buffer (no persistence), broadcast via SSE. |
GET /api/sessions / GET /api/sessions/stream |
Session lifecycle: list user-visible terminals; SSE pushes session_created / session_closed so the web UI tab bar stays in sync with agent activity. |
GET /api/chat/conversations |
Chat persistence: list saved sidebar conversations (also written to ~/.winkterm/chat_history.json). |
GET /api/settings/export |
Config backup: download full config.json (localhost or valid X-Access-Key). |
GET /api/agent/handshake |
Zero-config onboarding: localhost or web-auth'd clients get the token automatically. The agent doesn't need to ask the user every session. |
- Exit codes are first-class: no need to grep output to detect failure β
exit_codeis in the response. - 30+ named keys:
ctrl+c/up/tab/esc/f1β no raw control chars in JSON. - base64 input: complex awk / jq / heredoc commands go through
command_b64, sidestepping triple-escaping. - Persistent cwd tracking: the exec sentinel reports
$PWDafter every run; the monitoring panel displays the terminal's current directory. - TTL auto-cleanup: terminals default to 30-minute idle TTL, so forgotten terminals don't leak.
- wait reason field: distinguishes
idle/timeout/no_outputso callers know what happened.
Claude Code plugin (one-liner):
/plugin marketplace add Cznorth/winkterm
/plugin install winkterm-remote@winktermAny agent (raw skill from a running backend):
curl -s http://<your-winkterm-host>:8000/api/agent/skill.md > SKILL.mdDrop SKILL.md into Claude Code / Cursor / any agent tool's skills directory and the AI immediately knows how to drive the API. The skill is versioned β agents check for updates each session.
Internal craft agents and the external HTTP API share the same terminal session pool and tool surface (list / create / close / snapshot / input / exec / ssh_run). Agent-created terminals are user-visible and open as regular tabs in the main UI. Subscribe to /api/sessions/stream for live tab sync, or /api/agent/events/stream for a color-coded operation audit log.
π Case: AI agent locates and removes an XMR cryptojacker in 30 minutes via WinkTerm
A real incident write-up: user said only "the 107.173 server's load is high," and the AI agent completed discovery β investigation β kill chain reconstruction β hardening β abuse reporting end-to-end via the Agent API. The 9 new features in this release were reverse-engineered from the pain points hit during this exact case.
| Feature | WinkTerm | Warp | Tabby | Claude Code |
|---|---|---|---|---|
| Shared PTY (AI types in your terminal) | β | β | β | β |
| Open source | β | β | β | β |
| Self-hosted / BYO LLM | β | β | β | β |
| Codex OAuth (ChatGPT login) | β | β | β | β |
| Web UI | β | β | β | β (CLI only) |
| SSH + file transfer | β | β | β | β |
| Desktop app | β | β | β | β |
WinkTerm's core philosophy: The terminal is where operations happen. AI should live inside it, not beside it. When the AI writes a command into your input line and you press Enter, you're not blindly trusting β you're reviewing, understanding, and choosing. That's collaborative ops.
docker run -p 3000:3000 -p 8000:8000 \
-e ANTHROPIC_API_KEY=*** \
ghcr.io/cznorth/winkterm:latestOr with docker-compose:
git clone https://github.com/Cznorth/winkterm.git
cd winkterm
cp .env.example .env
# Edit .env with your API keys
docker compose up -dThe compose file mounts a winkterm-data volume at /root/.winkterm, so config, chat history, and SSH credentials survive container rebuilds. The image also bundles the installable agent skill (no 404 on skill.md fetch).
Then open http://localhost:3000
Download the latest release for your platform from the Releases page.
- Windows:
.exeinstaller - macOS:
.appbundle (Intel & Apple Silicon). The desktop build starts the embedded backend before opening the WebView and avoids baking dev-onlylocalhost:8000into static assets.
| Variable | Description | Default |
|---|---|---|
ANTHROPIC_API_KEY |
Anthropic API key (required) | β |
OPENAI_API_KEY |
OpenAI API key (alternative) | β |
MODEL_NAME |
Model to use | claude-sonnet-4-20250514 |
OPENAI_BASE_URL |
Custom API endpoint | β |
AGENT_RECURSION_LIMIT |
Agent recursion limit | 100 |
PROMETHEUS_URL |
Prometheus endpoint | http://localhost:9090 |
LOKI_URL |
Loki endpoint | http://localhost:3100 |
DEBUG |
Enable debug mode | false |
Bring your own LLM: WinkTerm uses the OpenAI-compatible protocol. Set
OPENAI_BASE_URLto any provider (Ollama, vLLM, Groq, OpenRouter, etc.) and WinkTerm will use it.
Codex OAuth (UI): No env vars required β enable Codex OAuth in Settings and complete browser login. Status:
GET /api/codex/status; start login:POST /api/codex/oauth/start.
User Keyboard Input
β
βΌ
Frontend Terminal (xterm.js)
β WebSocket
βΌ
ws_handler.py
β
βββ Normal input βββΊ pty_manager.write() βββΊ shell process
β
βββ Lines starting with # βββΊ intercept βββΊ Agent (LangGraph)
β
βββ get_terminal_context()
βββ terminal_input()
βββ write_command() βββΊ pty βββΊ terminal input line
Key insight: AI messages are written directly into the PTY output stream, so they appear seamlessly in your terminal. No separate UI chrome, no context switching.
| Layer | Technology |
|---|---|
| Backend | Python + FastAPI + LangGraph + LangChain |
| Frontend | Next.js 14 + TypeScript + xterm.js |
| Database-less | ~/.winkterm/config.json + chat_history.json on disk |
| Deployment | Docker Compose / PyInstaller desktop app |
- Python 3.12+
- Node.js 20+
- Docker (optional)
cd backend
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
python -m uvicorn backend.main:app --reload --port 8000cd frontend
npm install
npm run devIn Cursor, use the built-in browser MCP against http://localhost:3000 (click .xterm-screen, read .xterm-rows via CDP). Elsewhere, use puppeteer-core with system Chrome β see CLAUDE.md for the full smoke checklist and agent HTTP curl recipes.
Requires local frontend/backend, system Chrome, and ffmpeg. Uses your ~/.winkterm/config.json (theme, language, SSH connections).
cd scripts && npm install
node record-readme-normal.mjs # β assets/demo.gif
node record-promo-normal.mjs # β assets/promo.mp4
node capture-og-image.mjs # β assets/og-image-social.png (from demo final frame)Slow down an existing GIF without re-recording: REBUILD_GIF_ONLY=1 GIF_FRAME_SEC=1.4 node record-readme-normal.mjs
# With the backend running
cd frontend
npm run gen:api- Vim/Neovim integration (AI writes inside buffers)
- Terminal recording & replay (as replays)
- Multi-Agent orchestration (parallel ops)
- Plugin system for custom tools
- Native tmux integration
- Kubernetes context awareness
We welcome contributions! See CONTRIBUTING.md for guidelines.
Ideas for first PRs:
- Improve error messages and edge-case handling
- Add more agent tools (kubectl, docker, git helpers)
- Write tests (backend is undertested)
- Improve the xterm.js theme/color scheme
- Add language support for agent prompts
- LinuxDo β a community of people who love technology
MIT Β© 2026 Cznorth
Made with β€οΈ by Cznorth
Report Bug β’ Discussion β’ Star History β’ Share on Twitter
