From f91d3644fbee17371a1a83abfbe86d555467f5b6 Mon Sep 17 00:00:00 2001 From: mika kuns Date: Fri, 24 Apr 2026 19:22:45 +0200 Subject: [PATCH] feat(service): implement install-service verb --- src/ClaudeMailbox/Cli/ServiceCommands.cs | 67 +++++++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/src/ClaudeMailbox/Cli/ServiceCommands.cs b/src/ClaudeMailbox/Cli/ServiceCommands.cs index 16ca8dd..1fd12e8 100644 --- a/src/ClaudeMailbox/Cli/ServiceCommands.cs +++ b/src/ClaudeMailbox/Cli/ServiceCommands.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Runtime.Versioning; +using System.Security.AccessControl; using System.Security.Principal; namespace ClaudeMailbox.Cli; @@ -49,8 +50,70 @@ public static class ServiceCommands var admin = RequireAdmin(); if (admin != 0) return admin; - Console.Error.WriteLine("install-service: not yet implemented."); - return 1; + var programData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData); + var dataDir = Path.Combine(programData, "ClaudeMailbox"); + var configPath = Path.Combine(dataDir, "mailbox.json"); + var defaultDbPath = Path.Combine(dataDir, "mailbox.db"); + + Directory.CreateDirectory(dataDir); + ApplyLocalServiceAcl(dataDir); + + if (!File.Exists(configPath)) + { + var port = ClientCommands.GetOption(args, "--port") ?? "47822"; + var bind = ClientCommands.GetOption(args, "--bind") ?? "127.0.0.1"; + var dbPath = ClientCommands.GetOption(args, "--db-path") ?? defaultDbPath; + + var json = $$""" + { + "port": {{port}}, + "bind": "{{bind}}", + "dbPath": {{System.Text.Json.JsonSerializer.Serialize(dbPath)}} + } + """; + File.WriteAllText(configPath, json); + Console.WriteLine($"Seeded config: {configPath}"); + } + else + { + Console.WriteLine($"Config already exists, leaving untouched: {configPath}"); + } + + var exe = Environment.ProcessPath + ?? throw new InvalidOperationException("Cannot resolve current executable path."); + + var binPath = $"\"{exe}\" serve --config \"{configPath}\""; + + var createExit = RunSc("create", ServiceName, + "binPath=", binPath, + "start=", "auto", + "DisplayName=", "Claude Mailbox", + "obj=", "NT AUTHORITY\\LocalService"); + if (createExit != 0) + { + Console.Error.WriteLine($"sc create failed (exit {createExit})."); + return createExit; + } + + RunSc("description", ServiceName, "MCP mailbox server for parallel Claude sessions"); + + Console.WriteLine($"Service '{ServiceName}' installed. Start with: claude-mailbox start"); + return 0; + } + + [SupportedOSPlatform("windows")] + private static void ApplyLocalServiceAcl(string path) + { + var info = new DirectoryInfo(path); + var security = info.GetAccessControl(); + var localService = new SecurityIdentifier(WellKnownSidType.LocalServiceSid, null); + security.AddAccessRule(new FileSystemAccessRule( + localService, + FileSystemRights.Modify | FileSystemRights.Synchronize, + InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, + PropagationFlags.None, + AccessControlType.Allow)); + info.SetAccessControl(security); } [SupportedOSPlatform("windows")]