- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用 - 添加所有子项目的完整源代码 - 保留原始 .git 为 .git.bak 备份
9.4 KiB
In-App Terminals
Nomi ships a real terminal inside the app. Each terminal is a backend-managed PTY session you can drive interactively from your browser/desktop window — and that AutoWork can drive on your behalf when you bind it to a tag.
Need the automation guide? See AutoWork & Requirements. Need to run an agent on a schedule? See Scheduled Tasks.
What an in-app terminal is
When you create a terminal, the backend (nomifun-terminal) spawns a child
process attached to a real pseudo-terminal via portable-pty. The session
has three pieces:
- Persistent metadata — id, name, working directory, command + args, env, preset/backend, permission mode, current size (cols × rows), pinned flag, exit status. Stored in SQLite so the session entry survives restarts.
- A live PTY (only while the child is running) — the OS pseudo-terminal, its byte-stream output, and a scrollback buffer the backend keeps for late joiners.
- Realtime events on the WebSocket bus — every chunk of PTY output is
base64-encoded and broadcast as
terminal.output. Lifecycle events (terminal.created,terminal.updated,terminal.exit,terminal.removed) ride the same bus. The xterm.js view in the renderer subscribes and renders the stream.
A PTY child cannot be paused or moved between processes: when the child exits, the row stays but the live PTY is gone. Re-launching is in-place — the same session id keeps a fresh process attached, so you do not get a new sidebar entry every time you restart a CLI.
Creating a terminal
Open the Terminal create page (the + button in the terminal sidebar
section, or navigate to /terminal-new). You pick five things:
-
Workspace — the working directory the child process will be spawned in. Recent workspaces are remembered.
-
Preset —
Shell,Claude Code,Codex, orGemini. The shell preset resolves to your platform's login shell at launch time (Windows: PowerShell/cmd, macOS/Linux:$SHELL); the agent presets launch the matching CLI binary that must already be installed and onPATH. -
Permission mode (agent presets only) —
Default(interactive approvals) orFull Auto(the CLI's own non-interactive flag is added):Preset Full-auto flag claude--dangerously-skip-permissionscodex--dangerously-bypass-approvals-and-sandboxgemini--yoloThese bypass the CLI's interactive approval prompt — needed for AutoWork to drive a turn end-to-end without a human pressing Enter, but the same flags give the CLI broad capability on your machine. Treat full-auto terminals like a logged-in shell.
-
Launch command — the dialog renders the resolved
command + argsinto an editable field. Tweak it freely (extra flags, alternative entry point, etc.) before pressing Launch. -
Knowledge bases (optional) — multi-select one or more knowledge bases to bind to this session. Bound bases are mounted at
{workspace}/.nomi/knowledge/before the child spawns, together with a generatedREADME.md(retrieval protocol + per-base digests + TOC + write-back rules); theclaudepreset additionally gets an--append-system-promptpointer to that README. Rebinding takes effect on the next re-launch. (The gateway toolnomi_create_terminalaccepts the same binding viaknowledge_base_ids.)
The backend persists the row and spawns the child. The page navigates to
/terminal/<id> and you start receiving live output.
Driving a terminal
The session page is xterm.js wired to the realtime stream:
- Type to send keystrokes to the PTY. The send box also accepts paste with bracketed-paste markers, so multi-line text becomes one paste rather than a flurry of Enters.
- Resize the panel and the backend resizes the PTY accordingly (
SIGWINCHis delivered to the child). The new dimensions are persisted. - Re-launch when the child has exited: a single button kills any leftover
PTY for the same id, spawns a fresh process with the stored command + cwd
- env, clears the view, and the same
terminal.<id>subscription picks up the new output. You keep the same sidebar entry.
- env, clears the view, and the same
- Rename / pin from the session header (renames broadcast as
terminal.updated; pinned terminals float to the top of the sidebar). - Kill stops the child but keeps the row (it transitions to
exitedand becomes re-launchable). Delete kills the child and removes the row entirely.
Streaming model
Output flows over a single WebSocket. While you are looking at a session, your
client receives terminal.output events for that id and renders them. The
backend keeps a scrollback buffer in memory while the PTY is live: when you
open a terminal that is already running, the GET response includes a
base64-encoded scrollback_b64 snapshot, so xterm replays history before live
events stream in.
Client-to-server input goes the other direction over a small REST endpoint (base64-encoded bytes). The backend writes those bytes straight to the PTY's stdin.
Terminals as automation targets
The same in-memory PTY map that powers the UI is shared with the AutoWork
orchestrator in nomifun-requirement via the TerminalDriver trait. That
trait lets AutoWork:
- Subscribe to a copy of the terminal's live output (it watches for completion markers and detects quiescence — see the AutoWork guide for the contract).
- Write input bytes to the PTY (it injects the requirement prompt wrapped in bracketed-paste so a multi-line instruction lands as a single paste).
- Check liveness, read the row's metadata (user, backend, mode), and read or
write a per-terminal
autoworkconfig blob.
In other words: a terminal you create here is automatable by AutoWork.
Bind a tag from the AutoWork toolbar in the session header, and the
orchestrator will start claiming requirements and feeding them to the CLI
running in this terminal. Only agent-CLI terminals (claude, codex,
gemini) are eligible — a plain shell can be driven manually but is not an
AutoWork target. The orchestrator also recommends Full Auto mode, because a
turn that hits an interactive approval prompt will block until it times out.
If the workspace has knowledge bases mounted ({cwd}/.nomi/knowledge/
exists), AutoWork- and cron-driven prompts are automatically prefixed with a
one-line hint pointing the CLI at the mounted README.md before it starts
working.
If the PTY exits while AutoWork is still bound, the loop does not stop — it idles and waits for you to re-launch the terminal, then resumes claiming where it left off. If you delete the row, the loop stops for good.
IDMM (decision-stall supervision)
Long-running CLI sessions sometimes stall: the provider drops, the model spins on a tool call, the CLI prints a confirmation prompt nobody answers. The IDMM (Intelligent Decision-Making Mode) supervisor watches a session and intervenes — first with rule-based nudges (no LLM), then by calling a sidecar backup model — so the turn reaches a terminal state instead of hanging until the AutoWork timeout fires.
You can enable IDMM per-terminal from the same session header (the IDMM control next to AutoWork). It works whether or not AutoWork is also bound; when both are on, AutoWork ensures IDMM is supervising for the duration of each turn.
Routes & API
| What | Where |
|---|---|
| Create page | /terminal-new |
| Session page | /terminal/:id |
| List / create | GET /api/terminals, POST /api/terminals |
| Get / update / delete | `GET |
| Send input | POST /api/terminals/:id/input |
| Resize | POST /api/terminals/:id/resize |
| Kill child | POST /api/terminals/:id/kill |
| Re-launch in place | POST /api/terminals/:id/relaunch |
| Live output / lifecycle | WebSocket events terminal.* |
Troubleshooting
- The CLI is not found. The agent presets call
claude,codex, orgeminidirectly — they must be on thePATHof whatever account is running the backend. Either install the CLI globally or edit the launch command to use an absolute path before launching. - AutoWork bind is greyed out. Only
claude/codexterminals are AutoWork targets today. A plain shell preset cannot be bound, and Gemini terminal AutoWork is not wired into the backend completion contract yet. - Re-launch keeps reusing the same env / cwd. That is intentional — the session row stores them. To change them, create a new terminal with the desired settings.
- The output is garbled after resize. Some TUIs need a redraw on
SIGWINCH. PressCtrl-L(or your CLI's redraw shortcut).


