const std = @import("std"); const Config = @import("../config.zig").Config; pub fn run(allocator: std.mem.Allocator, args: []const []const u8) !void { for (args) |arg| { if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) { printUsage(); return; } if (std.mem.eql(u8, arg, "--json")) { std.debug.print("monitor does not support --json\n", .{}); printUsage(); return error.InvalidArgs; } } const config = try Config.load(allocator); defer { var mut_config = config; mut_config.deinit(allocator); } std.debug.print("Launching TUI via SSH...\n", .{}); // Build remote command that exports config via env vars and runs the TUI var remote_cmd_buffer = std.ArrayList(u8){}; defer remote_cmd_buffer.deinit(allocator); { const writer = remote_cmd_buffer.writer(allocator); try writer.print("cd {s} && ", .{config.worker_base}); try writer.print( "FETCH_ML_CLI_HOST=\"{s}\" FETCH_ML_CLI_USER=\"{s}\" FETCH_ML_CLI_BASE=\"{s}\" ", .{ config.worker_host, config.worker_user, config.worker_base }, ); try writer.print( "FETCH_ML_CLI_PORT=\"{d}\" FETCH_ML_CLI_API_KEY=\"{s}\" ", .{ config.worker_port, config.api_key }, ); try writer.writeAll("./bin/tui"); for (args) |arg| { try writer.print(" {s}", .{arg}); } } const remote_cmd = try remote_cmd_buffer.toOwnedSlice(allocator); defer allocator.free(remote_cmd); const ssh_cmd = try std.fmt.allocPrint( allocator, "ssh -t -p {d} {s}@{s} '{s}'", .{ config.worker_port, config.worker_user, config.worker_host, remote_cmd }, ); defer allocator.free(ssh_cmd); var child = std.process.Child.init(&[_][]const u8{ "sh", "-c", ssh_cmd }, allocator); child.stdin_behavior = .Inherit; child.stdout_behavior = .Inherit; child.stderr_behavior = .Inherit; const term = try child.spawnAndWait(); if (term.tag == .Exited and term.Exited != 0) { std.debug.print("TUI exited with code {d}\n", .{term.Exited}); } } fn printUsage() void { std.debug.print("Usage: ml monitor [-- ]\n\n", .{}); std.debug.print("Launches the remote TUI over SSH using ~/.ml/config.toml\n", .{}); }