fetch_ml/cli/src/commands/status.zig
Jeremie Fraeys d225ea1f00 feat: implement Zig CLI with comprehensive ML experiment management
- Add modern CLI interface built with Zig for performance
- Include TUI (Terminal User Interface) with bubbletea-like features
- Implement ML experiment commands (run, status, manage)
- Add configuration management and validation
- Include shell completion scripts for bash and zsh
- Add comprehensive CLI testing framework
- Support for multiple ML frameworks and project types

CLI provides fast, efficient interface for ML experiment management
with modern terminal UI and comprehensive feature set.
2025-12-04 16:53:58 -05:00

95 lines
3.2 KiB
Zig

const std = @import("std");
const Config = @import("../config.zig").Config;
const ws = @import("../net/ws.zig");
const crypto = @import("../utils/crypto.zig");
const errors = @import("../errors.zig");
const logging = @import("../utils/logging.zig");
const UserContext = struct {
name: []const u8,
admin: bool,
allocator: std.mem.Allocator,
pub fn deinit(self: *UserContext) void {
self.allocator.free(self.name);
}
};
fn authenticateUser(allocator: std.mem.Allocator, config: Config) !UserContext {
// Validate API key by making a simple API call to the server
const ws_url = try std.fmt.allocPrint(allocator, "ws://{s}:9101/ws", .{config.worker_host});
defer allocator.free(ws_url);
// Try to connect with the API key to validate it
var client = ws.Client.connect(allocator, ws_url, config.api_key) catch |err| {
switch (err) {
error.ConnectionRefused => return error.ConnectionFailed,
error.NetworkUnreachable => return error.ServerUnreachable,
error.InvalidURL => return error.ConfigInvalid,
else => return error.AuthenticationFailed,
}
};
defer client.close();
// For now, create a user context after successful authentication
// In a real implementation, this would get user info from the server
const user_name = try allocator.dupe(u8, "authenticated_user");
return UserContext{
.name = user_name,
.admin = false,
.allocator = allocator,
};
}
pub fn run(allocator: std.mem.Allocator, args: []const []const u8) !void {
_ = args;
// Load configuration with proper error handling
const config = Config.load(allocator) catch |err| {
switch (err) {
error.FileNotFound => return error.ConfigNotFound,
else => return err,
}
};
defer {
var mut_config = config;
mut_config.deinit(allocator);
}
// Check if API key is configured
if (config.api_key.len == 0) {
return error.APIKeyMissing;
}
// Authenticate with server to get user context
var user_context = try authenticateUser(allocator, config);
defer user_context.deinit();
// Use plain password for WebSocket authentication, compute hash for binary protocol
const api_key_plain = config.api_key; // Plain password from config
const api_key_hash = try crypto.hashString(allocator, api_key_plain);
defer allocator.free(api_key_hash);
// Connect to WebSocket and request status
const ws_url = std.fmt.allocPrint(allocator, "ws://{s}:9101/ws", .{config.worker_host}) catch |err| {
return err;
};
defer allocator.free(ws_url);
var client = ws.Client.connect(allocator, ws_url, api_key_plain) catch |err| {
switch (err) {
error.ConnectionRefused => return error.ConnectionFailed,
error.NetworkUnreachable => return error.ServerUnreachable,
error.InvalidURL => return error.ConfigInvalid,
else => return err,
}
};
defer client.close();
client.sendStatusRequest(api_key_hash) catch {
return error.RequestFailed;
};
// Receive and display user-filtered response
try client.receiveAndHandleStatusResponse(allocator, user_context);
}