Commit a25e2fe
Eric Bower
·
2026-05-20 22:21:35 -0400 EDT
parent d5c6926
refactor(run): marker for heredoc For most commands we can append `; $?` except for heredoc where the marker needs to be on its own line.
1 files changed,
+28,
-14
+28,
-14
| ... | ... | @@ -1140,17 +1140,33 @@ const Daemon = struct { | |
| 1140 | 1140 | ||
| 1141 | 1141 | const cmd = payload; | |
| 1142 | 1142 | ||
| 1143 | - | // Daemon appends the task marker so we know when a task is done with | |
| 1144 | - | // exit status | |
| 1145 | - | const marker = "echo ZMX_TASK_COMPLETED:$?\r"; | |
| 1146 | - | ||
| 1143 | + | // Redirect stdin from /dev/null to prevent interactive programs | |
| 1144 | + | // (pagers, editors, prompts) from blocking the task. Programs that | |
| 1145 | + | // detect a TTY and open a pager (e.g. git, man) or read stdin | |
| 1146 | + | // (e.g. cat, head) will receive EOF instead of blocking. This | |
| 1147 | + | // matches the behavior of CI runners and `tmux send-keys`. | |
| 1148 | + | // | |
| 1149 | + | // Commands that legitimately need stdin should use `zmx send` | |
| 1150 | + | // instead, or pipe data directly: `echo data | zmx run dev cat`. | |
| 1151 | + | // Here-documents still work because the shell processes the | |
| 1152 | + | // here-document before applying the /dev/null redirection. | |
| 1153 | + | const stdin_redirect = "< /dev/null "; | |
| 1154 | + | ||
| 1155 | + | // Chain the exit marker with `;` on the same line. `$?` captures the | |
| 1156 | + | // exit code of the command (not the `;`). The sole exception is when | |
| 1157 | + | // the command contains a heredoc (`<<`) — the delimiter must be alone | |
| 1158 | + | // on its line, so the marker goes on the next line instead. | |
| 1159 | + | const single_line_marker = "; echo ZMX_TASK_COMPLETED:$?\r"; | |
| 1160 | + | const heredoc_marker = "\r\necho ZMX_TASK_COMPLETED:$?\r"; | |
| 1161 | + | const uses_heredoc = std.mem.indexOf(u8, cmd, "<<") != null; | |
| 1162 | + | ||
| 1163 | + | self.queuePtyInput(stdin_redirect); | |
| 1147 | 1164 | if (cmd.len > 0 and cmd[cmd.len - 1] == '\r') { | |
| 1148 | 1165 | self.queuePtyInput(cmd[0 .. cmd.len - 1]); | |
| 1149 | 1166 | } else { | |
| 1150 | 1167 | self.queuePtyInput(cmd); | |
| 1151 | 1168 | } | |
| 1152 | - | self.queuePtyInput("\r"); | |
| 1153 | - | self.queuePtyInput(marker); | |
| 1169 | + | self.queuePtyInput(if (uses_heredoc) heredoc_marker else single_line_marker); | |
| 1154 | 1170 | ||
| 1155 | 1171 | try ipc.appendMessage(self.alloc, &client.write_buf, .Ack, ""); | |
| 1156 | 1172 | client.has_pending_output = true; |
| ... | ... | @@ -1282,13 +1298,10 @@ fn help() !void { | |
| 1282 | 1298 | \\ Commands run inside a PTY using bash | |
| 1283 | 1299 | \\ Commands are passed as-is: do not wrap in quotes. | |
| 1284 | 1300 | \\ Commands run sequentially: do not send multiple in parallel. | |
| 1285 | - | \\ Avoid interactive programs (pagers, editors, prompts): they hang. | |
| 1286 | - | \\ | |
| 1287 | - | \\ If the command hangs, send Ctrl+C to recover: | |
| 1288 | - | \\ zmx run <session> $(printf '\x03') | |
| 1289 | - | \\ | |
| 1290 | - | \\ If the command hangs, print the history to see the error: | |
| 1291 | - | \\ zmx history <session> | tail -100 | |
| 1301 | + | \\ Stdin is redirected from /dev/null to prevent interactive programs | |
| 1302 | + | \\ (pagers, editors, prompts) from blocking. Use `zmx send` for | |
| 1303 | + | \\ commands that need user input, or pipe data directly: | |
| 1304 | + | \\ echo "data" | zmx run dev cat | |
| 1292 | 1305 | \\ | |
| 1293 | 1306 | \\ `-d` will detach from the calling terminal. Use `wait` to track | |
| 1294 | 1307 | \\ its status. |
| ... | ... | @@ -1297,7 +1310,8 @@ fn help() !void { | |
| 1297 | 1310 | \\ zmx run dev ls | |
| 1298 | 1311 | \\ zmx run dev zig build | |
| 1299 | 1312 | \\ zmx run dev grep -r TODO src | |
| 1300 | - | \\ zmx run dev git -c core.pager=cat diff | |
| 1313 | + | \\ zmx run dev git log --oneline # pager won't block | |
| 1314 | + | \\ echo "hello" | zmx run dev cat # piped stdin still works | |
| 1301 | 1315 | \\ | |
| 1302 | 1316 | \\ zmx run dev -d sleep 10 | |
| 1303 | 1317 | \\ zmx wait dev |