using System.Text.Json;
using System.Text.Json.Serialization;
using ClaudeDo.Data;
namespace ClaudeDo.Worker.Config;
public sealed class WorkerConfig
{
[JsonPropertyName("db_path")]
public string DbPath { get; set; } = "~/.todo-app/todo.db";
[JsonPropertyName("sandbox_root")]
public string SandboxRoot { get; set; } = "~/.todo-app/sandbox";
[JsonPropertyName("log_root")]
public string LogRoot { get; set; } = "~/.todo-app/logs";
/// "sibling" → place worktrees next to the target repo; "central" → under .
[JsonPropertyName("worktree_root_strategy")]
public string WorktreeRootStrategy { get; set; } = "sibling";
[JsonPropertyName("central_worktree_root")]
public string CentralWorktreeRoot { get; set; } = "~/.todo-app/worktrees";
[JsonPropertyName("queue_backstop_interval_ms")]
public int QueueBackstopIntervalMs { get; set; } = 30_000;
[JsonPropertyName("signalr_port")]
public int SignalRPort { get; set; } = 47_821;
[JsonPropertyName("claude_bin")]
public string ClaudeBin { get; set; } = "claude";
/// Port for the external MCP endpoint. 0 disables the external listener entirely.
[JsonPropertyName("external_mcp_port")]
public int ExternalMcpPort { get; set; } = 47_822;
/// Optional API key clients must pass via X-ClaudeDo-Key header. Null/empty = loopback trust only.
[JsonPropertyName("external_mcp_api_key")]
public string? ExternalMcpApiKey { get; set; }
public static string DefaultConfigPath =>
Path.Combine(Paths.AppDataRoot(), "worker.config.json");
///
/// Loads the config from (defaults to ).
/// Missing file → returns defaults. Resolves all path-typed fields to absolute paths.
///
public static WorkerConfig Load(string? path = null)
{
path ??= DefaultConfigPath;
WorkerConfig cfg;
if (File.Exists(path))
{
var json = File.ReadAllText(path);
cfg = JsonSerializer.Deserialize(json, JsonOpts)
?? throw new InvalidOperationException($"Failed to parse {path}");
}
else
{
cfg = new WorkerConfig();
}
cfg.DbPath = Paths.Expand(cfg.DbPath);
cfg.SandboxRoot = Paths.Expand(cfg.SandboxRoot);
cfg.LogRoot = Paths.Expand(cfg.LogRoot);
cfg.CentralWorktreeRoot = Paths.Expand(cfg.CentralWorktreeRoot);
return cfg;
}
private static readonly JsonSerializerOptions JsonOpts = new()
{
ReadCommentHandling = JsonCommentHandling.Skip,
AllowTrailingCommas = true,
};
}