fix: complete bugfixes for UI, OAuth fallbacks, cliRuntime Windows constraints and Codex non-streaming integration

This commit is contained in:
diegosouzapw
2026-03-30 11:01:55 -03:00
parent 5ad687c6d8
commit f810b13bca
4 changed files with 41 additions and 18 deletions

View File

@@ -151,7 +151,7 @@ export function translateNonStreamingResponse(
if (toolCalls.length > 0) {
message.tool_calls = toolCalls;
}
if (!message.content && !message.tool_calls) {
if (message.content === undefined) {
message.content = "";
}
@@ -352,7 +352,7 @@ export function translateNonStreamingResponse(
if (toolCalls.length > 0) {
message.tool_calls = toolCalls;
}
if (!message.content && !message.tool_calls) {
if (message.content === undefined) {
message.content = "";
}
@@ -410,18 +410,31 @@ function convertOpenAINonStreamingToClaude(openaiResponse: JsonRecord): JsonReco
const content = [];
let hasTextOrReasoning = false;
if (messageObj.reasoning_content) {
hasTextOrReasoning = true;
content.push({
type: "thinking",
thinking: toString(messageObj.reasoning_content),
});
}
if (messageObj.content) {
// Always include text if it exists (even empty string), or if there are no tool calls and no reasoning
const hasToolCalls = Array.isArray(messageObj.tool_calls) && messageObj.tool_calls.length > 0;
if (messageObj.content !== undefined && messageObj.content !== null) {
hasTextOrReasoning = true;
content.push({
type: "text",
text: toString(messageObj.content),
});
} else if (!hasTextOrReasoning) {
// Claude format expects a text block even before tool calls (or if empty)
content.push({
type: "text",
text: "",
});
}
if (Array.isArray(messageObj.tool_calls)) {

View File

@@ -211,7 +211,12 @@ export default function ProviderLimits() {
USAGE_SUPPORTED_PROVIDERS.includes(conn.provider) &&
(conn.authType === "oauth" || conn.authType === "apikey")
);
await Promise.all(usageConnections.map((conn) => fetchQuota(conn.id, conn.provider)));
// Fix Issue #784: Fetch quotas in chunks of 5 to avoid spamming the backend/provider APIs and hanging the UI.
const chunkSize = 5;
for (let i = 0; i < usageConnections.length; i += chunkSize) {
const chunk = usageConnections.slice(i, i + chunkSize);
await Promise.all(chunk.map((conn) => fetchQuota(conn.id, conn.provider)));
}
setLastUpdated(new Date());
} catch (error) {
console.error("Error refreshing all:", error);

View File

@@ -211,6 +211,8 @@ export default function OAuthModal({
return;
}
let forceManual = false;
// Codex: on localhost use callback server on port 1455,
// on remote use standard auth code flow (callback server is unreachable)
if (provider === "codex") {
@@ -252,11 +254,13 @@ export default function OAuthModal({
setPolling(false);
throw new Error("Authorization timeout");
} catch (codexErr) {
console.warn(
"Codex callback server failed, falling back to standard manual flow",
codexErr
);
setPolling(false);
setStep("input");
setError(codexErr.message + " — You can paste the callback URL manually below.");
forceManual = true;
}
return;
}
// Remote: fall through to standard auth code flow below
}
@@ -306,8 +310,8 @@ export default function OAuthModal({
setAuthData({ ...data, redirectUri });
// For non-true-localhost (LAN IPs, remote): use manual input mode (user pastes callback URL)
if (!isTrueLocalhost) {
// For non-true-localhost (LAN IPs, remote) or manual fallback: use manual input mode (user pastes callback URL)
if (!isTrueLocalhost || forceManual) {
setStep("input");
window.open(data.authUrl, "oauth_auth");
} else {

View File

@@ -328,10 +328,7 @@ const getExpectedParentPaths = (): string[] => {
const npmPrefix = getNpmGlobalPrefix();
// Add common user bin directories
const userBinPaths = [
path.join(home, "bin"),
path.join(home, ".local", "bin"),
];
const userBinPaths = [path.join(home, "bin"), path.join(home, ".local", "bin")];
return [
home,
@@ -531,11 +528,15 @@ const locateCommand = async (command: string, env: Record<string, string | undef
if (!located.ok || !located.stdout) {
return { installed: false, commandPath: null, reason: "not_found" };
}
const first =
located.stdout
.split(/\r?\n/)
.map((line) => normalizeMsys2Path(line.trim()))
.find(Boolean) || null;
const lines = located.stdout
.split(/\r?\n/)
.map((line) => normalizeMsys2Path(line.trim()))
.filter(Boolean);
// Issue #809: Prioritize executable wrappers (.cmd, .exe, .bat) over extensionless bash scripts
// that NPM often drops alongside the wrappers in global installs.
const first = lines.find((line) => /\.(cmd|exe|bat)$/i.test(line)) || lines[0] || null;
return { installed: !!first, commandPath: first, reason: first ? null : "not_found" };
}