chore: improve desktop renderer diagnostics

Serialize non-Error promise rejections so unhandled rejections print type/ctor/keys/JSON instead of the unreadable '[object Object]'. Also emit [server health] logs when a health poll returns unhealthy and when polling switches servers, so a red dot in the status popover comes with a logged URL and auth presence. Minor cosmetic: restore session-header StatusPopover import position after the earlier titlebar experiment.
This commit is contained in:
LukeParkerDev 2026-04-17 14:51:41 +10:00
parent 8fd7bd19d6
commit da2e640029
3 changed files with 49 additions and 4 deletions

View file

@ -6,6 +6,7 @@ import { IconButton } from "@opencode-ai/ui/icon-button"
import { Keybind } from "@opencode-ai/ui/keybind"
import { Spinner } from "@opencode-ai/ui/spinner"
import { showToast } from "@opencode-ai/ui/toast"
import { StatusPopover } from "../status-popover"
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
import { getFilename } from "@opencode-ai/shared/util/path"
import { createEffect, createMemo, For, onCleanup, onMount, Show } from "solid-js"
@ -24,7 +25,6 @@ import { useSessionLayout } from "@/pages/session/session-layout"
import { messageAgentColor } from "@/utils/agent"
import { decode64 } from "@/utils/base64"
import { Persist, persisted } from "@/utils/persist"
import { StatusPopover } from "../status-popover"
const OPEN_APPS = [
"vscode",

View file

@ -200,7 +200,20 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext(
const isReady = createMemo(() => ready() && !!state.active)
const check = (conn: ServerConnection.Any) => checkServerHealth(conn.http).then((x) => x.healthy)
const check = (conn: ServerConnection.Any) =>
checkServerHealth(conn.http).then((x) => {
if (!x.healthy) {
// Loud: makes it trivial to see why a server shows red in the
// status popover / switcher. The dot only goes red when this
// returns false; otherwise undefined (gray) is emitted first.
console.warn("[server health] unhealthy", {
key: ServerConnection.key(conn),
url: conn.http.url,
hasAuth: !!(conn.http.username || conn.http.password),
})
}
return x.healthy
})
createEffect(() => {
const current_ = current()
@ -211,6 +224,10 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext(
return
}
setState("healthy", undefined)
console.log("[server health] start polling", {
key: ServerConnection.key(current_),
url: current_.http.url,
})
onCleanup(startHealthPolling(current_))
})

View file

@ -29,8 +29,36 @@ window.addEventListener("error", (event) => {
window.addEventListener("unhandledrejection", (event) => {
const reason = event.reason
const stack = reason instanceof Error ? reason.stack : null
console.error("[renderer unhandled rejection]", stack ?? reason)
// Log as much as possible: stack for Errors, JSON for plain objects with
// a fallback to a tagged shape so we never end up with just
// "[object Object]" in main.log.
if (reason instanceof Error) {
console.error("[renderer unhandled rejection]", reason.stack ?? reason.message ?? String(reason))
return
}
let serialized: string
try {
serialized = JSON.stringify(
reason,
(_key, value) => {
if (value instanceof Error) {
return { __error: true, name: value.name, message: value.message, stack: value.stack }
}
return value
},
2,
)
} catch {
serialized = String(reason)
}
console.error(
"[renderer unhandled rejection]",
`type=${typeof reason}`,
`ctor=${reason?.constructor?.name ?? "null"}`,
`keys=${reason && typeof reason === "object" ? Object.keys(reason).join(",") : "n/a"}`,
"value:",
serialized,
)
})
import {