const std = @import("std"); const Config = @import("../config.zig").Config; const db = @import("../db.zig"); const core = @import("../core.zig"); pub fn run(allocator: std.mem.Allocator, args: []const []const u8) !void { var flags = core.flags.CommonFlags{}; var remaining = try core.flags.parseCommon(allocator, args, &flags); defer remaining.deinit(allocator); core.output.setMode(if (flags.json) .json else .text); // Handle help flag early if (flags.help) { return printUsage(); } // Parse CLI-specific overrides and flags const cli_tracking_uri = core.flags.parseKVFlag(remaining.items, "tracking-uri"); const cli_artifact_path = core.flags.parseKVFlag(remaining.items, "artifact-path"); const cli_sync_uri = core.flags.parseKVFlag(remaining.items, "sync-uri"); const force_local = core.flags.parseBoolFlag(remaining.items, "local"); const cli_mode = core.flags.parseKVFlag(remaining.items, "mode"); var cfg = try Config.loadWithOverrides(allocator, cli_tracking_uri, cli_artifact_path, cli_sync_uri); defer cfg.deinit(allocator); // Apply mode preference from --mode flag if (cli_mode) |mode| { if (std.mem.eql(u8, mode, "local")) { cfg.execution_mode = .local; } else if (std.mem.eql(u8, mode, "remote")) { cfg.execution_mode = .remote; } else { cfg.execution_mode = .auto; } } // Print resolved config std.debug.print("Resolved config:\n", .{}); std.debug.print("\ttracking_uri = {s}", .{cfg.tracking_uri}); // Indicate if using default if (cli_tracking_uri == null and std.mem.eql(u8, cfg.tracking_uri, "sqlite://./fetch_ml.db")) { std.debug.print("\t(default)\n", .{}); } else { std.debug.print("\n", .{}); } std.debug.print("\tartifact_path = {s}", .{cfg.artifact_path}); if (cli_artifact_path == null and std.mem.eql(u8, cfg.artifact_path, "./experiments/")) { std.debug.print("\t(default)\n", .{}); } else { std.debug.print("\n", .{}); } std.debug.print("\tsync_uri = {s}\n", .{if (cfg.sync_uri.len > 0) cfg.sync_uri else "(not set)"}); std.debug.print("\texecution_mode = {s}\n", .{switch (cfg.execution_mode) { .local => "local", .remote => "remote", .auto => "auto", }}); std.debug.print("\n", .{}); // Default path: create config only (no DB speculatively) if (!force_local) { std.debug.print("Created .fetchml/config.toml\n", .{}); std.debug.print("\tLocal tracking DB will be created automatically if server becomes unavailable.\n", .{}); if (cfg.sync_uri.len > 0) { std.debug.print("\tServer: {s}:{d}\n", .{ cfg.worker_host, cfg.worker_port }); } return; } // --local path: create config + DB now std.debug.print("(local mode explicitly requested)\n\n", .{}); // Get DB path from tracking URI const db_path = try cfg.getDBPath(allocator); defer allocator.free(db_path); // Check if DB already exists const db_exists = blk: { std.fs.accessAbsolute(db_path, .{}) catch |err| { if (err == error.FileNotFound) break :blk false; }; break :blk true; }; if (db_exists) { std.debug.print("Database already exists: {s}\n", .{db_path}); } else { // Create parent directories if needed if (std.fs.path.dirname(db_path)) |dir| { std.fs.makeDirAbsolute(dir) catch |err| { if (err != error.PathAlreadyExists) { std.log.err("Failed to create directory {s}: {}", .{ dir, err }); return error.MkdirFailed; } }; } // Initialize database (creates schema) var database = try db.DB.init(allocator, db_path); defer database.close(); defer database.checkpointOnExit(); std.debug.print("Created database: {s}\n", .{db_path}); } std.debug.print("Created .fetchml/config.toml\n", .{}); std.debug.print("Schema applied (WAL mode enabled)\n", .{}); std.debug.print("\tfetch_ml.db-wal and fetch_ml.db-shm will appear during use — expected.\n", .{}); std.debug.print("\tThe DB is just a file. Delete it freely — recreated automatically on next run.\n", .{}); } fn printUsage() void { std.debug.print("Usage: ml init [OPTIONS]\n\n", .{}); std.debug.print("Initialize FetchML configuration\n\n", .{}); std.debug.print("Options:\n", .{}); std.debug.print("\t--local\t\t\tCreate local database now (default: config only)\n", .{}); std.debug.print("\t--mode MODE\t\tDefault execution mode: local, remote, or auto (default: auto)\n", .{}); std.debug.print("\t--tracking-uri URI\tSQLite database path (e.g., sqlite://./fetch_ml.db)\n", .{}); std.debug.print("\t--artifact-path PATH\tArtifacts directory (default: ./experiments/)\n", .{}); std.debug.print("\t--sync-uri URI\t\tServer to sync with (e.g., wss://ml.company.com/ws)\n", .{}); std.debug.print("\t-h, --help\t\tShow this help\n", .{}); }