const std = @import("std"); const Config = @import("../../config.zig").Config; const ws = @import("../../net/ws/client.zig"); const crypto = @import("../../utils/crypto.zig"); const protocol = @import("../../net/protocol.zig"); /// List Jupyter services pub fn listJupyter(allocator: std.mem.Allocator, args: []const []const u8, json: bool) !void { _ = args; _ = json; try listServices(allocator); } /// Show Jupyter service status pub fn statusJupyter(allocator: std.mem.Allocator, args: []const []const u8, json: bool) !void { _ = args; _ = json; // Re-use listServices for now as status is part of list try listServices(allocator); } /// Internal function to list all services fn listServices(allocator: std.mem.Allocator) !void { const config = try Config.load(allocator); defer { var mut_config = config; mut_config.deinit(allocator); } const url = try config.getWebSocketUrl(allocator); defer allocator.free(url); var client = ws.Client.connect(allocator, url, config.api_key) catch |err| { std.debug.print("Failed to connect to server: {}\n", .{err}); return; }; defer client.close(); const api_key_hash = try crypto.hashApiKey(allocator, config.api_key); defer allocator.free(api_key_hash); client.sendListJupyter(api_key_hash) catch |err| { std.debug.print("Failed to send list command: {}\n", .{err}); return; }; const response = client.receiveMessage(allocator) catch |err| { std.debug.print("Failed to receive response: {}\n", .{err}); return; }; defer allocator.free(response); const packet = protocol.ResponsePacket.deserialize(response, allocator) catch |err| { std.debug.print("Failed to parse response: {}\n", .{err}); return; }; defer packet.deinit(allocator); switch (packet.packet_type) { .data => { std.debug.print("Jupyter Services:\n", .{}); if (packet.data_payload) |payload| { const parsed = std.json.parseFromSlice(std.json.Value, allocator, payload, .{}) catch { std.debug.print("{s}\n", .{payload}); return; }; defer parsed.deinit(); var services_opt: ?std.json.Array = null; if (parsed.value == .array) { services_opt = parsed.value.array; } else if (parsed.value == .object) { if (parsed.value.object.get("services")) |sv| { if (sv == .array) services_opt = sv.array; } } if (services_opt == null) { std.debug.print("{s}\n", .{payload}); return; } const services = services_opt.?; if (services.items.len == 0) { std.debug.print("No running services.\n", .{}); return; } std.debug.print("NAME\t\t\t\t\t\t\t\t\t\t\tSTATUS\t\tURL\t\t\t\t\t\t\t\t\t\t\t\t\tWORKSPACE\n", .{}); std.debug.print("---- ------ --- ---------\n", .{}); for (services.items) |item| { if (item != .object) continue; const obj = item.object; var name: []const u8 = ""; if (obj.get("name")) |v| { if (v == .string) name = v.string; } var status: []const u8 = ""; if (obj.get("status")) |v| { if (v == .string) status = v.string; } var url_str: []const u8 = ""; if (obj.get("url")) |v| { if (v == .string) url_str = v.string; } var workspace: []const u8 = ""; if (obj.get("workspace")) |v| { if (v == .string) workspace = v.string; } std.debug.print("{s: <20} {s: <9} {s: <25} {s}\n", .{ name, status, url_str, workspace }); } } }, .error_packet => { const error_msg = protocol.ResponsePacket.getErrorMessage(packet.error_code.?); std.debug.print("Failed to list services: {s}\n", .{error_msg}); if (packet.error_details) |details| { std.debug.print("Details: {s}\n", .{details}); } else if (packet.error_message) |msg| { std.debug.print("Details: {s}\n", .{msg}); } }, else => { std.debug.print("Unexpected response type\n", .{}); }, } } /// Package management commands pub fn packageCommands(args: []const []const u8) !void { if (args.len < 1) { std.debug.print("Usage: ml jupyter package \n", .{}); return; } const subcommand = args[0]; if (std.mem.eql(u8, subcommand, "list")) { if (args.len < 2) { std.debug.print("Usage: ml jupyter package list \n", .{}); return; } var service_name: []const u8 = ""; if (std.mem.eql(u8, args[1], "--name") and args.len >= 3) { service_name = args[2]; } else { service_name = args[1]; } if (service_name.len == 0) { std.debug.print("Service name is required\n", .{}); return; } const allocator = std.heap.page_allocator; const config = try Config.load(allocator); defer { var mut_config = config; mut_config.deinit(allocator); } const url = try config.getWebSocketUrl(allocator); defer allocator.free(url); var client = ws.Client.connect(allocator, url, config.api_key) catch |err| { std.debug.print("Failed to connect to server: {}\n", .{err}); return; }; defer client.close(); const api_key_hash = try crypto.hashApiKey(allocator, config.api_key); defer allocator.free(api_key_hash); client.sendListJupyterPackages(service_name, api_key_hash) catch |err| { std.debug.print("Failed to send list packages command: {}\n", .{err}); return; }; const response = client.receiveMessage(allocator) catch |err| { std.debug.print("Failed to receive response: {}\n", .{err}); return; }; defer allocator.free(response); const packet = protocol.ResponsePacket.deserialize(response, allocator) catch |err| { std.debug.print("Failed to parse response: {}\n", .{err}); return; }; defer packet.deinit(allocator); switch (packet.packet_type) { .data => { std.debug.print("Installed packages for {s}:\n", .{service_name}); if (packet.data_payload) |payload| { const parsed = std.json.parseFromSlice(std.json.Value, allocator, payload, .{}) catch { std.debug.print("{s}\n", .{payload}); return; }; defer parsed.deinit(); if (parsed.value != .array) { std.debug.print("{s}\n", .{payload}); return; } const pkgs = parsed.value.array; if (pkgs.items.len == 0) { std.debug.print("No packages found.\n", .{}); return; } std.debug.print("NAME VERSION SOURCE\n", .{}); std.debug.print("---- ------- ------\n", .{}); for (pkgs.items) |item| { if (item != .object) continue; const obj = item.object; var name: []const u8 = ""; if (obj.get("name")) |v| { if (v == .string) name = v.string; } var version: []const u8 = ""; if (obj.get("version")) |v| { if (v == .string) version = v.string; } var source: []const u8 = ""; if (obj.get("source")) |v| { if (v == .string) source = v.string; } std.debug.print("{s: <30} {s: <22} {s}\n", .{ name, version, source }); } } }, .error_packet => { const error_msg = protocol.ResponsePacket.getErrorMessage(packet.error_code.?); std.debug.print("Failed to list packages: {s}\n", .{error_msg}); if (packet.error_details) |details| { std.debug.print("Details: {s}\n", .{details}); } else if (packet.error_message) |msg| { std.debug.print("Details: {s}\n", .{msg}); } }, else => { std.debug.print("Unexpected response type\n", .{}); }, } } else { std.debug.print("Invalid package command: {s}\n", .{subcommand}); } }