ProcessService: allocate a terminal (PTY) for interactive processes
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 42/100
Research direction
Start from the ProcessService definitions and the StartProcess handler, and read how stdin, stdout and stderr are wired to plain pipes and how output is spooled for StreamProcessOutput. Add the optional TerminalSize field 6 and the ResizeProcessTerminal RPC, then check that a process started with a terminal gets a controlling PTY, that SIGWINCH is delivered on resize, and that the no-terminal path is unchanged.
Written by the indexing model from the issue text.
Description
StartProcess always runs the command with plain pipes for stdin, stdout and stderr. There's no
way to ask for a terminal, so clients can't offer an interactive session. Without a terminal,
programs in the guest have:
- no prompt, job control or line editing; arrow keys arrive as
^[[A - no full-screen programs (
vim,top,less), becauseisatty()is false and there is no
window size - Ctrl-C handled by the client, which has to translate it to
SignalProcess(SIGINT), instead
of by the terminal inside the guest
Clients that need a terminal include:
- Interactive shells: for example, AX's
ax ssh <task>
opens a shell in a running task through this API. - Web and desktop terminals: dashboards or IDEs that embed a terminal (xterm.js) for a
sandbox. - Agents driving interactive CLIs: REPLs, debuggers and tools that change their behavior or
refuse to run without a TTY.
Today, clients work around this by wrapping the command in script(1) to create a PTY inside the
guest and passing the window size with stty rows R cols C. AX's desktop app does this:
stty raw -echo
ax ssh task -- sh -c 'exec env TERM=xterm-256color script -qefc \
"stty rows $0 cols $1; exec bash -i" /dev/null' "$(tput lines)" "$(tput cols)"
stty sane
This depends on script being in the image, ignores window resizes after startup, and every
client has to reinvent it.
Proposal
Add an optional terminal to StartProcessRequest, and an RPC to resize it.
message TerminalSize {
// Rows and columns of the terminal window. Both must be > 0.
uint32 rows = 1;
uint32 cols = 2;
}
message StartProcessRequest {
// ... existing fields 1-5 ...
// If set, the process runs with a pseudo-terminal of this size as its
// controlling terminal and its stdin, stdout and stderr. Requires stdin.
// All output arrives as ProcessOutput.stdout; stderr is unused.
TerminalSize size = 6;
}
service ProcessService {
// ... existing RPCs ...
// ResizeProcessTerminal changes the window size of a process started with
// a terminal, and delivers SIGWINCH to its foreground process group.
// Fails with FAILED_PRECONDITION if the process has no terminal.
rpc ResizeProcessTerminal(ResizeProcessTerminalRequest) returns (Process);
}
message ResizeProcessTerminalRequest {
string process_id = 1;
TerminalSize size = 2;
}
Semantics
- Startup: the guest opens a PTY, sets its size, makes it the process's controlling terminal
(setsidplusTIOCSCTTY), and connects the PTY to stdin, stdout and stderr. - Input:
WriteProcessInputwrites to the PTY. Control characters such as Ctrl-C (0x03) and
Ctrl-Z go through the terminal, which sends the signal itself, so clients just forward raw
keystrokes.closecloses the input side, and the process sees EOF or a hangup. - Output: everything the PTY emits is spooled as stdout, so
StreamProcessOutputoffsets and
replay keep working. stderr stays empty. - Environment:
TERMis unchanged. Clients set it throughenv, e.g.
TERM=xterm-256color. - No terminal requested: behavior is the same as today.
Example client: ax ssh
When ax ssh runs in a terminal, it would:
- read the window size, and put the local terminal in raw mode;
- call
StartProcesswithstdin: true,terminal: {rows, cols}and
env: {TERM: $TERM}; - forward keystrokes with
WriteProcessInput, and callResizeProcessTerminalon SIGWINCH; - restore the local terminal when the process exits.
A web terminal does the same thing, using xterm.js's onData and onResize events.
Alternatives considered
script(1)wrapper (current workaround): depends on the image, sizes the terminal only
once, and has to be duplicated in every client.- Resize sent in-band through stdin: mixes control data with user input and can't carry the
size reliably.
Related to #107.
- Dominant language
- Go
- Stars
- 70
- Forks
- 17
- Avg merge
- 21h 36m
- Merged PRs (30d)
- 19
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from agent-substrate/env
-
Difficulty 3/5 1-2 days Newbie friendliness 74/100
agent-substrate/env#121 ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 42/100
agent-substrate/env#119 ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 55/100
agent-substrate/env#118 ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 57/100
agent-substrate/env#117 ·
Maintainers usually reply within 1 day
-
bug substrate
Difficulty 4/5 3-5 days Newbie friendliness 35/100
agent-substrate/env#115 ·
Maintainers usually reply within 1 day
All issues in agent-substrate/env
Similar issues
-
Fix mode unpins a reusable workflow call when the same repository is also used as a step actionOpen
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
github/gh-actions-lock#261 ·
Maintainers usually reply within 1 day
-
bug documentation
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
labmonkeys-space/nl6#974 ·
Maintainers usually reply within 1 day
-
model:sonnet phase-2-optimize runtime:claude security size:S
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
FootprintAI/Containarium#3471 ·
Maintainers usually reply within 1 day
-
[BUG] serviceenvupdater filter does not update KUBERNETES_SERVICE_HOST/PORT in init containersPossibly taken @RaphaelFakhri claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
openyurtio/openyurt#2845 ·
Maintainers usually reply within 7 days
-
fuzz.yml skips three fuzz targets, and the Action's user input describes only ~/.claudePossibly taken @predictor2718 claimed this today. Opengithub_actions P3 tests
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day