Commit 87cc98f
Eric Bower
·
2026-03-03 20:32:47 -0500 EST
parent d12b7cc
fix(run): stdin regression with ZMX_TASK_COMPLETED We were not actually running the command sent into stdin properly. Closes: https://github.com/neurosnap/zmx/issues/71
1 files changed,
+29,
-27
+29,
-27
| ... | ... | @@ -445,18 +445,6 @@ pub fn main() !void { | |
| 445 | 445 | while (args.next()) |arg| { | |
| 446 | 446 | try cmd_args_raw.append(alloc, arg); | |
| 447 | 447 | } | |
| 448 | - | var cmd_args = try cmd_args_raw.clone(alloc); | |
| 449 | - | defer cmd_args.deinit(alloc); | |
| 450 | - | ||
| 451 | - | const shell = detectShell(); | |
| 452 | - | // add a task completed marker so we know when the cmd is finished | |
| 453 | - | // we also capture the exit status | |
| 454 | - | if (std.mem.eql(u8, std.fs.path.basename(shell), "fish")) { | |
| 455 | - | // fish has special handling for capturing exit status | |
| 456 | - | try cmd_args.append(alloc, "; echo ZMX_TASK_COMPLETED:$status"); | |
| 457 | - | } else { | |
| 458 | - | try cmd_args.append(alloc, "; echo ZMX_TASK_COMPLETED:$?"); | |
| 459 | - | } | |
| 460 | 448 | const clients = try std.ArrayList(*Client).initCapacity(alloc, 10); | |
| 461 | 449 | ||
| 462 | 450 | var cwd_buf: [std.fs.max_path_bytes]u8 = undefined; |
| ... | ... | @@ -480,7 +468,7 @@ pub fn main() !void { | |
| 480 | 468 | }; | |
| 481 | 469 | daemon.socket_path = try getSocketPath(alloc, cfg.socket_dir, sesh); | |
| 482 | 470 | std.log.info("socket path={s}", .{daemon.socket_path}); | |
| 483 | - | return run(&daemon, cmd_args.items); | |
| 471 | + | return run(&daemon, cmd_args_raw.items); | |
| 484 | 472 | } else if (std.mem.eql(u8, cmd, "wait") or std.mem.eql(u8, cmd, "w")) { | |
| 485 | 473 | var args_raw: std.ArrayList([]const u8) = .empty; | |
| 486 | 474 | defer { |
| ... | ... | @@ -1055,6 +1043,10 @@ fn run(daemon: *Daemon, command_args: [][]const u8) !void { | |
| 1055 | 1043 | var buf: [4096]u8 = undefined; | |
| 1056 | 1044 | var w = std.fs.File.stdout().writer(&buf); | |
| 1057 | 1045 | ||
| 1046 | + | var cmd_to_send: ?[]const u8 = null; | |
| 1047 | + | var allocated_cmd: ?[]u8 = null; | |
| 1048 | + | defer if (allocated_cmd) |cmd| alloc.free(cmd); | |
| 1049 | + | ||
| 1058 | 1050 | const result = try ensureSession(daemon); | |
| 1059 | 1051 | if (result.is_daemon) return; | |
| 1060 | 1052 |
| ... | ... | @@ -1063,9 +1055,16 @@ fn run(daemon: *Daemon, command_args: [][]const u8) !void { | |
| 1063 | 1055 | try w.interface.flush(); | |
| 1064 | 1056 | } | |
| 1065 | 1057 | ||
| 1066 | - | var cmd_to_send: ?[]const u8 = null; | |
| 1067 | - | var allocated_cmd: ?[]u8 = null; | |
| 1068 | - | defer if (allocated_cmd) |cmd| alloc.free(cmd); | |
| 1058 | + | const shell = detectShell(); | |
| 1059 | + | const shell_basename = std.fs.path.basename(shell); | |
| 1060 | + | const inline_task_marker = if (std.mem.eql(u8, shell_basename, "fish")) | |
| 1061 | + | "; echo ZMX_TASK_COMPLETED:$status" | |
| 1062 | + | else | |
| 1063 | + | "; echo ZMX_TASK_COMPLETED:$?"; | |
| 1064 | + | const stdin_task_marker = if (std.mem.eql(u8, shell_basename, "fish")) | |
| 1065 | + | "echo ZMX_TASK_COMPLETED:$status" | |
| 1066 | + | else | |
| 1067 | + | "echo ZMX_TASK_COMPLETED:$?"; | |
| 1069 | 1068 | ||
| 1070 | 1069 | if (command_args.len > 0) { | |
| 1071 | 1070 | var parts: std.ArrayList([]const u8) = .empty; |
| ... | ... | @@ -1075,11 +1074,8 @@ fn run(daemon: *Daemon, command_args: [][]const u8) !void { | |
| 1075 | 1074 | } | |
| 1076 | 1075 | ||
| 1077 | 1076 | var total_len: usize = 0; | |
| 1078 | - | for (command_args, 0..) |arg, i| { | |
| 1079 | - | // Last arg is the sentinel (e.g. "; echo ZMX_TASK_COMPLETED:$?") | |
| 1080 | - | // which must not be quoted so the shell interprets it. | |
| 1081 | - | const is_last = i == command_args.len - 1; | |
| 1082 | - | if (!is_last and shellNeedsQuoting(arg)) { | |
| 1077 | + | for (command_args) |arg| { | |
| 1078 | + | if (shellNeedsQuoting(arg)) { | |
| 1083 | 1079 | const quoted = try shellQuote(alloc, arg); | |
| 1084 | 1080 | try parts.append(alloc, quoted); | |
| 1085 | 1081 | total_len += quoted.len + 1; |
| ... | ... | @@ -1090,20 +1086,22 @@ fn run(daemon: *Daemon, command_args: [][]const u8) !void { | |
| 1090 | 1086 | } | |
| 1091 | 1087 | } | |
| 1092 | 1088 | ||
| 1089 | + | total_len += inline_task_marker.len + 1; | |
| 1090 | + | ||
| 1093 | 1091 | const cmd_buf = try alloc.alloc(u8, total_len); | |
| 1094 | 1092 | allocated_cmd = cmd_buf; | |
| 1095 | 1093 | ||
| 1096 | 1094 | var offset: usize = 0; | |
| 1097 | - | for (parts.items, 0..) |part, i| { | |
| 1095 | + | for (parts.items) |part| { | |
| 1098 | 1096 | @memcpy(cmd_buf[offset .. offset + part.len], part); | |
| 1099 | 1097 | offset += part.len; | |
| 1100 | - | if (i < parts.items.len - 1) { | |
| 1101 | - | cmd_buf[offset] = ' '; | |
| 1102 | - | } else { | |
| 1103 | - | cmd_buf[offset] = '\n'; | |
| 1104 | - | } | |
| 1098 | + | cmd_buf[offset] = ' '; | |
| 1105 | 1099 | offset += 1; | |
| 1106 | 1100 | } | |
| 1101 | + | ||
| 1102 | + | @memcpy(cmd_buf[offset .. offset + inline_task_marker.len], inline_task_marker); | |
| 1103 | + | offset += inline_task_marker.len; | |
| 1104 | + | cmd_buf[offset] = '\n'; | |
| 1107 | 1105 | cmd_to_send = cmd_buf; | |
| 1108 | 1106 | } else { | |
| 1109 | 1107 | const stdin_fd = posix.STDIN_FILENO; |
| ... | ... | @@ -1126,6 +1124,10 @@ fn run(daemon: *Daemon, command_args: [][]const u8) !void { | |
| 1126 | 1124 | if (needs_newline) { | |
| 1127 | 1125 | try stdin_buf.append(alloc, '\n'); | |
| 1128 | 1126 | } | |
| 1127 | + | ||
| 1128 | + | try stdin_buf.appendSlice(alloc, stdin_task_marker); | |
| 1129 | + | try stdin_buf.append(alloc, '\n'); | |
| 1130 | + | ||
| 1129 | 1131 | cmd_to_send = try alloc.dupe(u8, stdin_buf.items); | |
| 1130 | 1132 | allocated_cmd = @constCast(cmd_to_send.?); | |
| 1131 | 1133 | } |