Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src-tauri/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ const REMOTE_BUILD_FILES: &[&str] = &[
"proxyd/Cargo.toml",
"proxyd/Cargo.lock",
"proxyd/src/main.rs",
"src/app_paths.rs",
"src/auth.rs",
"src/models.rs",
"src/profile_files.rs",
"src/proxy_daemon.rs",
"src/proxy_service.rs",
"src/state.rs",
Expand Down
2 changes: 2 additions & 0 deletions src-tauri/src/remote_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ const PROXYD_BUILD_SOURCE_FILES: &[&str] = &[
"proxyd/Cargo.toml",
"proxyd/Cargo.lock",
"proxyd/src/main.rs",
"src/app_paths.rs",
"src/auth.rs",
"src/models.rs",
"src/profile_files.rs",
"src/proxy_daemon.rs",
"src/proxy_service.rs",
"src/state.rs",
Expand Down
32 changes: 32 additions & 0 deletions src-tauri/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,11 +282,43 @@ fn preferred_executable_dir_candidates() -> Vec<PathBuf> {
] {
push_unique_candidate(&mut dirs, dir);
}

#[cfg(target_os = "windows")]
add_winget_package_executable_dirs(&mut dirs, &home);
}

dirs
}

#[cfg(target_os = "windows")]
fn add_winget_package_executable_dirs(dirs: &mut Vec<PathBuf>, home: &Path) {
let packages_dir = home
.join("AppData")
.join("Local")
.join("Microsoft")
.join("WinGet")
.join("Packages");
let Ok(packages) = fs::read_dir(packages_dir) else {
return;
};

for package in packages.flatten() {
let package_path = package.path();
let Some(package_name) = package_path.file_name().and_then(|name| name.to_str()) else {
continue;
};
if !package_name.starts_with("zig.zig_") {
continue;
}
push_unique_candidate(dirs, package_path.clone());
if let Ok(children) = fs::read_dir(package_path) {
for child in children.flatten() {
push_unique_candidate(dirs, child.path());
}
}
}
}

fn push_unique_dir(dirs: &mut Vec<PathBuf>, candidate: PathBuf) {
if candidate.is_dir() && !dirs.iter().any(|existing| existing == &candidate) {
dirs.push(candidate);
Expand Down
91 changes: 91 additions & 0 deletions src/hooks/useCodexController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,8 @@ export function useCodexController() {
const settingsRef = useRef<AppSettings>(DEFAULT_SETTINGS);
const apiProxyUsageLoadSeqRef = useRef(0);
const apiProxyUsagePollInFlightRef = useRef(false);
const remoteProxyStatusesRawRef = useRef<Record<string, RemoteProxyStatus>>({});
const remoteProxyAutoRedeployInFlightRef = useRef(false);
const reloginPromptedAccountKeysRef = useRef<Set<string>>(new Set());
const profileIntegrityPromptedRef = useRef(false);

Expand Down Expand Up @@ -327,6 +329,10 @@ export function useCodexController() {
[localizeRemoteProxyStatus, remoteProxyStatusesRaw],
);

useEffect(() => {
remoteProxyStatusesRawRef.current = remoteProxyStatusesRaw;
}, [remoteProxyStatusesRaw]);

const loadAccounts = useCallback(async () => {
const data = await invoke<AccountSummary[]>("list_accounts");
applyAccounts(data);
Expand Down Expand Up @@ -523,6 +529,7 @@ export function useCodexController() {
const successCount = result.importedCount + result.updatedCount;
if (successCount > 0) {
await loadAccounts();
void redeployDeployedRemoteProxiesAfterAccountChange();
}

if (successCount > 0 && result.failures.length === 0) {
Expand Down Expand Up @@ -1042,6 +1049,7 @@ export function useCodexController() {
await invoke<AccountSummary>("import_current_auth_account", { label: null });
await refreshUsage(true);
await loadAccounts();
void redeployDeployedRemoteProxiesAfterAccountChange();
setAddDialogOpen(false);
setNotice({ type: "ok", message: copy.notices.currentAccountImportSuccess });
} catch (error) {
Expand Down Expand Up @@ -1088,6 +1096,7 @@ export function useCodexController() {
try {
await invoke<AccountSummary>("create_api_account", { input });
await loadAccounts();
void redeployDeployedRemoteProxiesAfterAccountChange();
setAddDialogOpen(false);
setNotice({
type: "ok",
Expand Down Expand Up @@ -1404,6 +1413,86 @@ export function useCodexController() {
}
}, [copy.notices, deployingRemoteProxyId, ensureRemoteLocalDependency, localizeError]);

async function redeployDeployedRemoteProxiesAfterAccountChange() {
if (remoteProxyAutoRedeployInFlightRef.current) {
return;
}

const statuses = remoteProxyStatusesRawRef.current;
const servers = settingsRef.current.remoteServers.filter((server) => {
const status = statuses[server.id];
return Boolean(
status?.installed ||
status?.serviceInstalled ||
status?.running ||
status?.enabled,
);
});

if (servers.length === 0) {
return;
}

remoteProxyAutoRedeployInFlightRef.current = true;
const failures: Array<{ server: RemoteServerConfig; error: string }> = [];

try {
for (const server of servers) {
if (!(await ensureRemoteLocalDependency(server))) {
continue;
}

setRemoteDeployProgress({
serverId: server.id,
label: server.label,
stage: "validating",
progress: 6,
detail: null,
});
setDeployingRemoteProxyId(server.id);

try {
const status = await invoke<RemoteProxyStatus>("deploy_remote_proxy", {
input: {
server,
},
});
setRemoteProxyStatusesRaw((current) => ({
...current,
[server.id]: status,
}));
} catch (error) {
const errorText = String(error);
failures.push({ server, error: errorText });
setRemoteProxyStatusesRaw((current) => ({
...current,
[server.id]: buildRemoteProxyFallback(server, errorText),
}));
} finally {
setRemoteDeployProgress((current) =>
current?.serverId === server.id ? null : current,
);
setDeployingRemoteProxyId((current) =>
current === server.id ? null : current,
);
}
}
} finally {
remoteProxyAutoRedeployInFlightRef.current = false;
}

if (failures.length > 0) {
const firstFailure = failures[0];
setNotice({
type: "error",
message: copy.notices.remoteProxyDeployFailed(
firstFailure.server.label,
localizeError(firstFailure.error),
),
});
}
}

const onStartRemoteProxy = useCallback(async (server: RemoteServerConfig) => {
if (startingRemoteProxyId === server.id) {
return;
Expand Down Expand Up @@ -1729,6 +1818,7 @@ export function useCodexController() {
try {
await invoke<void>("delete_account", { id: account.id });
setAccounts((prev) => prev.filter((item) => item.id !== account.id));
void redeployDeployedRemoteProxiesAfterAccountChange();
setNotice({ type: "ok", message: copy.notices.accountDeleted });
} catch (error) {
setNotice({
Expand All @@ -1750,6 +1840,7 @@ export function useCodexController() {
restartEditorTargets: settings.restartEditorTargets,
});
await loadAccounts();
void redeployDeployedRemoteProxiesAfterAccountChange();

let baseNotice: Notice;
if (!settings.launchCodexAfterSwitch) {
Expand Down