Quick app, providing access to documentation, a text editor, and terminal in one place, with an AI agent to tie them together.
Local web app: Flask backend, HTML templates (Jinja2), CSS under app/static/css/, and React (JSX) built with Vite into app/static/dist/ for pages that opt in.
The home page includes a chat bar at the bottom of the main (left) column built with MUI X Chat (@mui/x-chat) and Material UI. It talks to Flask POST /api/chat, which streams newline-delimited JSON chunks compatible with MUI X Chat’s stream processor.
@mui/x-chat is alpha on npm; APIs may change between releases. Pin or upgrade deliberately.
Requires Python 3.10+ and Node.js 18+.
From the project root:
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtOn Windows, activate with .venv\Scripts\activate.
cd frontend
npm install
npm run buildThe build writes app/static/dist/assets/main.js and main.css. Flask serves them as /static/dist/assets/.... Run npm run build whenever you change frontend/src/ before loading the app via Flask (unless you use the Vite dev server; see below).
@mui/x-chat depends on @mui/icons-material for some UI affordances; it is listed in frontend/package.json so Vite can bundle it.
Recommended (Flask CLI, reload on Python/template changes):
flask --app app:create_app run --debug --host 127.0.0.1Use --host 127.0.0.1 (not 0.0.0.0) so the sidebar IPython terminal (WebSocket + PTY) is not exposed on your LAN. The terminal runs shell code as the same OS user as Flask.
Alternatives:
python run.pypython -m appOpen http://127.0.0.1:5000/.
- The lower right sidebar runs a real
ipythonsession in the browser (xterm.js +/ws/terminalWebSocket + PTY). Requiresipythonfromrequirements.txt(installed with the venv). - Unix only (macOS / Linux). On Windows the WebSocket responds with an unsupported message instead of spawning a PTY.
- Security: this is arbitrary code execution as your Flask OS user. Treat it as localhost-only, single-user tooling. Do not expose it on the public internet without strong isolation, auth, and hardening.
- Implementation:
app/terminal_session.py,app/terminal_routes.py.
The app supports multiple terminal execution providers controlled by env vars.
TERMINAL_PROVIDER=local(default): local PTY +ipythonon the same machine as Flask (dev-only; localhost guarded).TERMINAL_PROVIDER=e2b: run the terminal inside an E2B sandbox (safer for deployment).TERMINAL_PROVIDER=disabled: disables the terminal WebSocket.
- Install deps:
pip install -r requirements.txt- Set env:
E2B_API_KEY: required by the E2B SDKTERMINAL_PROVIDER=e2bE2B_TEMPLATE_NAME=interactive-docs-ipython(recommended; see below)
The default E2B base sandbox may not include ipython. To ensure the terminal starts an IPython REPL without enabling outbound network at runtime, build a custom E2B template that bakes ipython in.
- Build the template:
python e2b/build_template.py- Then run the app with:
E2B_TEMPLATE_NAME=interactive-docs-ipythonE2B_ALLOW_INTERNET_ACCESS=0(default)
When TERMINAL_PROVIDER=e2b and E2B_ALLOW_INTERNET_ACCESS=1, server code can install pip packages into the
current browser session's sandbox using app.terminal_pip.pip_install_requirements_into_session_sandbox
(requirements as a list of strings; returns exit_code, stdout, stderr, and normalized_requirements on success).
There is no public HTTP endpoint for ad-hoc pip installs; wire installs through your chat or other backend flow.
When token/origin enforcement is enabled (defaults are secure for e2b):
- The frontend calls
POST /api/terminal/tokento mint a short-lived one-time token. - The frontend calls
POST /api/terminal/killon page hide / background to best-effort stop the session E2B sandbox. - The terminal WebSocket must connect to
/ws/terminal?token=.... - The server rejects mismatched
Originand invalid/expired tokens.
Config env vars:
TERMINAL_REQUIRE_TOKEN:1|0(defaults to1fore2b,0forlocal)TERMINAL_ENFORCE_ORIGIN:1|0(default1)TERMINAL_WS_TOKEN_TTL_SECONDS: token TTL (default60)
TERMINAL_MAX_SESSION_SECONDS: max WS session duration (default3600, set0to disable)TERMINAL_IDLE_TIMEOUT_SECONDS: idle timeout (default300, set0to disable)TERMINAL_MAX_INBOUND_BYTES: per-message size limit (default65536)
POST /api/chat— JSON body:{ "conversationId": "...", "message": { "id", "role", "parts" } }.- Response:
application/x-ndjsonstream; each line is one JSON object (start,text-delta,text-end,finish, …) for MUI X Chat. - Implementation:
app/chat.py(streaming stub; replace with a real model later).
- Rebuild on save + Flask: in
frontend/, runnpm run build:watchin one terminal and Flask in another. Refresh the browser after each build. - Vite dev server:
cd frontend && npm run dev(e.g. http://localhost:5173/).vite.config.jsproxies/apiand/wstohttp://127.0.0.1:5000— run Flask on port 5000 while using Vite for HMR. The Viteindex.htmlonly mounts React; for the full Jinja layout, use Flask.
app/__init__.py— application factorycreate_app()app/routes.py— routes (blueprintmain)app/chat.py—POST /api/chatstreaming stub (blueprintchat, prefix/api)app/terminal_routes.py— HTTP routes + WebSocket/ws/terminal(flask-sock) for the sidebar IPython PTYapp/templates/— Jinja; React mounts at#main-chat-rootinpage__chat-bar(bottom of the left column only)app/static/— CSS and Vite output underapp/static/dist/frontend/— Vite + React source,npm run build→app/static/dist/