Skip to content

Commit 3bd10d1

Browse files
committed
added support for windows wsl
1 parent d127c83 commit 3bd10d1

File tree

8 files changed

+298
-100
lines changed

8 files changed

+298
-100
lines changed

SemanticDeveloper/SemanticDeveloper/MainWindow.axaml.cs

Lines changed: 60 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,10 @@ public MainWindow()
156156
_cli.Command = _settings.Command;
157157
_cli.UseApiKey = _settings.UseApiKey;
158158
_cli.ApiKey = string.IsNullOrWhiteSpace(_settings.ApiKey) ? null : _settings.ApiKey;
159-
AppendCliLog($"System: Loaded CLI settings: '{_cli.Command}'{(string.IsNullOrWhiteSpace(_settings.SelectedProfile)?"":" • profile=" + _settings.SelectedProfile)}");
159+
_cli.UseWsl = OperatingSystem.IsWindows() && _settings.UseWsl;
160+
var loadProfileSuffix = string.IsNullOrWhiteSpace(_settings.SelectedProfile) ? string.Empty : " • profile=" + _settings.SelectedProfile;
161+
var loadWslSuffix = _cli.UseWsl ? " • WSL" : string.Empty;
162+
AppendCliLog($"System: Loaded CLI settings: '{_cli.Command}'{loadProfileSuffix}{loadWslSuffix}");
160163
SelectedProfile = _settings.SelectedProfile;
161164
_currentModel = TryExtractModelFromArgs(_cli.AdditionalArgs);
162165
_verboseLogging = _settings.VerboseLoggingEnabled;
@@ -1247,7 +1250,16 @@ private async Task CheckCodexVersionAsync()
12471250
var latest = CodexVersionService.TryReadLatestVersion();
12481251
if (!latest.Ok || string.IsNullOrWhiteSpace(latest.Version)) return;
12491252

1250-
var installed = await CodexVersionService.TryGetInstalledVersionAsync(_cli.Command, CurrentWorkspacePath ?? Directory.GetCurrentDirectory());
1253+
(bool Ok, string? Version, string? Error) installed;
1254+
if (_cli.UseWsl && OperatingSystem.IsWindows())
1255+
{
1256+
var psi = await _cli.BuildProcessStartInfoAsync(CurrentWorkspacePath ?? Directory.GetCurrentDirectory(), new[] { "--version" }, redirectStdIn: false);
1257+
installed = await CodexVersionService.TryGetInstalledVersionAsync(psi);
1258+
}
1259+
else
1260+
{
1261+
installed = await CodexVersionService.TryGetInstalledVersionAsync(_cli.Command, CurrentWorkspacePath ?? Directory.GetCurrentDirectory());
1262+
}
12511263
if (!installed.Ok || string.IsNullOrWhiteSpace(installed.Version)) return;
12521264

12531265
if (CodexVersionService.IsNewer(latest.Version, installed.Version))
@@ -2168,6 +2180,7 @@ private async Task RestartCliAsync()
21682180
_cli.AdditionalArgs = composed;
21692181

21702182
// Proactive authentication: API key login or interactive chat login
2183+
// 1) If user provided API key in settings, use it non-interactively
21712184
if (_cli.UseApiKey && !string.IsNullOrWhiteSpace(_cli.ApiKey))
21722185
{
21732186
try
@@ -2196,29 +2209,41 @@ private async Task RestartCliAsync()
21962209
}
21972210
else
21982211
{
2199-
try
2212+
// 2) No API key in settings: probe ~/.codex/auth.json
2213+
var authProbe = Services.CodexAuthService.ProbeAuth();
2214+
if (authProbe.Exists && authProbe.HasTokens)
22002215
{
2201-
AppendCliLog("System: Authenticating CLI via 'codex login'…");
2202-
var exit = await RunCodexAuthLoginAsync();
2203-
if (exit == 0)
2216+
// Tokens present — assume already authenticated; skip proactive login
2217+
AppendCliLog("System: Found existing Codex auth tokens; skipping login.");
2218+
}
2219+
else
2220+
{
2221+
// Regardless of API key present in auth.json, settings dictate NOT to use API key.
2222+
// Fall back to interactive login to avoid implicit API key usage.
2223+
try
22042224
{
2205-
AppendCliLog("System: CLI authenticated (chat login).");
2225+
AppendCliLog("System: Authenticating CLI via 'codex login'…");
2226+
var exit = await RunCodexAuthLoginAsync();
2227+
if (exit == 0)
2228+
{
2229+
AppendCliLog("System: CLI authenticated (chat login).");
2230+
}
2231+
else
2232+
{
2233+
AppendCliLog($"System: CLI login failed (exit {exit}).");
2234+
_cli.AdditionalArgs = prevArgs; // restore before abort
2235+
SessionStatus = "error";
2236+
return;
2237+
}
22062238
}
2207-
else
2239+
catch (Exception ex)
22082240
{
2209-
AppendCliLog($"System: CLI login failed (exit {exit}).");
2241+
AppendCliLog("System: CLI login error: " + ex.Message);
22102242
_cli.AdditionalArgs = prevArgs; // restore before abort
22112243
SessionStatus = "error";
22122244
return;
22132245
}
22142246
}
2215-
catch (Exception ex)
2216-
{
2217-
AppendCliLog("System: CLI login error: " + ex.Message);
2218-
_cli.AdditionalArgs = prevArgs; // restore before abort
2219-
SessionStatus = "error";
2220-
return;
2221-
}
22222247
}
22232248

22242249
await _cli.StartAsync(CurrentWorkspacePath, CancellationToken.None);
@@ -2489,24 +2514,15 @@ private async Task HandleUnauthorizedAsync()
24892514

24902515
private async Task<int> RunCodexAuthLoginAsync()
24912516
{
2492-
var cwd = HasWorkspace ? CurrentWorkspacePath : Directory.GetCurrentDirectory();
2517+
var cwd = HasWorkspace && !string.IsNullOrWhiteSpace(CurrentWorkspacePath)
2518+
? CurrentWorkspacePath!
2519+
: Directory.GetCurrentDirectory();
24932520

2494-
async Task<int> RunAsync(string args)
2521+
async Task<int> RunAsync(params string[] commandArgs)
24952522
{
24962523
try
24972524
{
2498-
var psi = new ProcessStartInfo
2499-
{
2500-
FileName = _cli.Command,
2501-
Arguments = args,
2502-
WorkingDirectory = cwd!,
2503-
RedirectStandardOutput = true,
2504-
RedirectStandardError = true,
2505-
RedirectStandardInput = true,
2506-
UseShellExecute = false,
2507-
CreateNoWindow = true
2508-
};
2509-
2525+
var psi = await _cli.BuildProcessStartInfoAsync(cwd, commandArgs, redirectStdIn: true);
25102526
using var p = new Process { StartInfo = psi, EnableRaisingEvents = true };
25112527

25122528
void HandleLine(string? line)
@@ -2569,31 +2585,20 @@ void HandleLine(string? line)
25692585
}
25702586

25712587
// Try modern subcommand first, then fallback
2572-
var exit = await RunAsync("auth login");
2588+
var exit = await RunAsync("auth", "login");
25732589
if (exit == 0) return exit;
25742590
// Fallback: some versions may use `login`
25752591
return await RunAsync("login");
25762592
}
25772593

25782594
private async Task<int> RunCodexLoginWithApiKeyAsync(string apiKey)
25792595
{
2580-
var cwd = HasWorkspace ? CurrentWorkspacePath : Directory.GetCurrentDirectory();
2596+
var cwd = HasWorkspace && !string.IsNullOrWhiteSpace(CurrentWorkspacePath)
2597+
? CurrentWorkspacePath!
2598+
: Directory.GetCurrentDirectory();
25812599
try
25822600
{
2583-
var psi = new ProcessStartInfo
2584-
{
2585-
FileName = _cli.Command,
2586-
WorkingDirectory = cwd!,
2587-
RedirectStandardOutput = true,
2588-
RedirectStandardError = true,
2589-
UseShellExecute = false,
2590-
CreateNoWindow = true
2591-
};
2592-
2593-
// Prefer: codex login --api-key <key>
2594-
psi.ArgumentList.Add("login");
2595-
psi.ArgumentList.Add("--api-key");
2596-
psi.ArgumentList.Add(apiKey);
2601+
var psi = await _cli.BuildProcessStartInfoAsync(cwd, new[] { "login", "--api-key", apiKey }, redirectStdIn: false);
25972602

25982603
using (var p = new Process { StartInfo = psi, EnableRaisingEvents = true })
25992604
{
@@ -2607,19 +2612,7 @@ private async Task<int> RunCodexLoginWithApiKeyAsync(string apiKey)
26072612
}
26082613

26092614
// Fallback: codex auth login --api-key <key>
2610-
var psi2 = new ProcessStartInfo
2611-
{
2612-
FileName = _cli.Command,
2613-
WorkingDirectory = cwd!,
2614-
RedirectStandardOutput = true,
2615-
RedirectStandardError = true,
2616-
UseShellExecute = false,
2617-
CreateNoWindow = true
2618-
};
2619-
psi2.ArgumentList.Add("auth");
2620-
psi2.ArgumentList.Add("login");
2621-
psi2.ArgumentList.Add("--api-key");
2622-
psi2.ArgumentList.Add(apiKey);
2615+
var psi2 = await _cli.BuildProcessStartInfoAsync(cwd, new[] { "auth", "login", "--api-key", apiKey }, redirectStdIn: false);
26232616

26242617
using var p2 = new Process { StartInfo = psi2, EnableRaisingEvents = true };
26252618
p2.OutputDataReceived += (_, ev) => { if (!string.IsNullOrWhiteSpace(ev.Data)) AppendCliLog(ev.Data!); };
@@ -2728,13 +2721,19 @@ private async void OnOpenCliSettingsClick(object? sender, Avalonia.Interactivity
27282721
ShowMcpResultsInLog = _settings.ShowMcpResultsInLog,
27292722
ShowMcpResultsOnlyWhenNoEdits = _settings.ShowMcpResultsOnlyWhenNoEdits,
27302723
Profiles = Services.CodexConfigService.TryGetProfiles(),
2731-
SelectedProfile = _settings.SelectedProfile
2724+
SelectedProfile = _settings.SelectedProfile,
2725+
UseWsl = _cli.UseWsl,
2726+
CanUseWsl = OperatingSystem.IsWindows()
27322727
};
27332728
dialog.DataContext = vm;
27342729
var result = await dialog.ShowDialog<CliSettings?>(this);
27352730
if (result is null) return;
27362731
_cli.Command = result.Command;
2737-
AppendCliLog($"System: CLI settings updated: '{_cli.Command}'{(string.IsNullOrWhiteSpace(result.SelectedProfile)?"":" • profile=" + result.SelectedProfile)} (--proto enabled)");
2732+
var profileSuffix = string.IsNullOrWhiteSpace(result.SelectedProfile) ? string.Empty : " • profile=" + result.SelectedProfile;
2733+
_settings.UseWsl = OperatingSystem.IsWindows() && result.UseWsl;
2734+
_cli.UseWsl = OperatingSystem.IsWindows() && _settings.UseWsl;
2735+
var wslSuffix = _cli.UseWsl ? " • WSL" : string.Empty;
2736+
AppendCliLog($"System: CLI settings updated: '{_cli.Command}'{profileSuffix}{wslSuffix} (--proto enabled)");
27382737
// persist settings
27392738
_settings.Command = _cli.Command;
27402739
// AdditionalArgs removed (profiles/config.toml driven)

SemanticDeveloper/SemanticDeveloper/Models/AppSettings.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ public class AppSettings
1111
public bool ShowMcpResultsInLog { get; set; } = true;
1212
public bool ShowMcpResultsOnlyWhenNoEdits { get; set; } = true;
1313
public string SelectedProfile { get; set; } = string.Empty;
14+
public bool UseWsl { get; set; } = false;
1415
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using System;
2+
using System.IO;
3+
using Newtonsoft.Json.Linq;
4+
5+
namespace SemanticDeveloper.Services;
6+
7+
public static class CodexAuthService
8+
{
9+
public static string GetAuthJsonPath()
10+
{
11+
try
12+
{
13+
var home = Environment.GetEnvironmentVariable("CODEX_HOME");
14+
string dir;
15+
if (!string.IsNullOrWhiteSpace(home)) dir = home!;
16+
else dir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".codex");
17+
return Path.Combine(dir, "auth.json");
18+
}
19+
catch
20+
{
21+
return Path.Combine(Directory.GetCurrentDirectory(), "auth.json");
22+
}
23+
}
24+
25+
// Reads auth.json and reports whether token-based auth exists and any api key value found
26+
public static (bool Exists, bool HasTokens, string? ApiKey) ProbeAuth()
27+
{
28+
try
29+
{
30+
var path = GetAuthJsonPath();
31+
if (!File.Exists(path)) return (false, false, null);
32+
var text = File.ReadAllText(path);
33+
if (string.IsNullOrWhiteSpace(text)) return (true, false, null);
34+
var obj = JObject.Parse(text);
35+
36+
string? apiKey = null;
37+
var apiVal = obj["OPENAI_API_KEY"];
38+
if (apiVal != null && apiVal.Type == JTokenType.String)
39+
{
40+
var s = apiVal.ToString();
41+
if (!string.IsNullOrWhiteSpace(s)) apiKey = s;
42+
}
43+
44+
bool hasTokens = false;
45+
if (obj["tokens"] is JObject tok)
46+
{
47+
var id = tok["id_token"]?.ToString();
48+
var access = tok["access_token"]?.ToString();
49+
hasTokens = !string.IsNullOrWhiteSpace(id) || !string.IsNullOrWhiteSpace(access);
50+
}
51+
52+
return (true, hasTokens, apiKey);
53+
}
54+
catch
55+
{
56+
return (false, false, null);
57+
}
58+
}
59+
}
60+

0 commit comments

Comments
 (0)