╭ changed ─────────────────╮ │src/serve_ssh_protocol.zig│ ╰──────────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────╮ │271 } │ │272 } │ │273 │ │274 - /// send bytes to the client as one or more CHANNEL_DATA packets, │ │275 - /// respecting the peer's max-packet size. if the remote window is │ │276 - /// exhausted, pump incoming SSH packets until a WINDOW_ADJUST arrives │ │277 - /// (background packets are processed for their side effects — incoming │ │278 - /// CHANNEL_DATA goes into incoming_buffer for a future SessionReader). │ │274 + /// send bytes to the client's stdout (CHANNEL_DATA). │ │275 pub fn writeBytes(self: *SessionCtx, bytes: []const u8) !void { │ │276 + try self.writeChannel(bytes, false); │ │277 + } │ │278 + │ │279 + /// send bytes to the client's stderr (CHANNEL_EXTENDED_DATA). use this for │ │280 + /// server messages on a git exec session, where stdout carries the pack │ │281 + /// protocol and stray text breaks the client's pkt-line parser. │ │282 + pub fn writeStderr(self: *SessionCtx, bytes: []const u8) !void { │ │283 + try self.writeChannel(bytes, true); │ │284 + } │ │285 + │ │286 + /// ship bytes as one or more stdout CHANNEL_DATA (or stderr │ │287 + /// CHANNEL_EXTENDED_DATA) packets, respecting the peer's max-packet size. │ │288 + /// if the remote window is exhausted, pump incoming SSH packets until a │ │289 + /// WINDOW_ADJUST arrives (background packets are processed for their side │ │290 + /// effects — incoming CHANNEL_DATA goes into incoming_buffer for a future │ │291 + /// SessionReader). │ │292 + fn writeChannel(self: *SessionCtx, bytes: []const u8, extended: bool) !void {│ │293 var rest = bytes; │ │294 while (rest.len > 0) { │ │295 while (self.channel.remote_window == 0) { │ ╰──────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │301 │ │302 var msg: std.ArrayList(u8) = .empty; │ │303 defer msg.deinit(self.allocator); │ │291 - try msg.append(self.allocator, SSH_MSG_CHANNEL_DATA); │ │304 + try msg.append(self.allocator, if (extended) SSH_MSG_CHANNEL_EXTENDED_DATA else SSH_MSG_CHANNEL_DATA);│ │305 try writeU32(&msg, self.allocator, self.channel.remote_id); │ │306 + if (extended) try writeU32(&msg, self.allocator, SSH_EXTENDED_DATA_STDERR); │ │307 try writeStringField(&msg, self.allocator, rest[0..chunk_len]); │ │308 try self.sc_cipher.writePacket(self.io, self.allocator, self.writer, msg.items); │ │309 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯