fix(tui): stabilize reload connection state
This commit is contained in:
parent
24ab17e718
commit
8dd993d25a
16 changed files with 224 additions and 19 deletions
|
|
@ -1148,7 +1148,7 @@ function App(props: { onSnapshot?: () => Promise<string[]>; pluginHost: TuiPlugi
|
|||
<Show when={!startup.skipInitialLoading}>
|
||||
<StartupLoading ready={ready} />
|
||||
</Show>
|
||||
<Show when={sdk.connection.status() === "reconnecting"}>
|
||||
<Show when={sdk.connection.status() === "connecting"}>
|
||||
<Reconnecting attempt={sdk.connection.attempt()} error={sdk.connection.error()} />
|
||||
</Show>
|
||||
</box>
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
|
|||
directory: process.cwd(),
|
||||
})
|
||||
const messageIndex = new Map<string, Map<string, number>>()
|
||||
let bootstrapping: Promise<void> | undefined
|
||||
|
||||
const message = {
|
||||
update(sessionID: string, fn: (messages: SessionMessage[], index: Map<string, number>) => void) {
|
||||
|
|
@ -543,6 +544,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
|
|||
// Authenticating an MCP integration reconnects its server, which emits mcp.status.changed,
|
||||
// so the mcp list refreshes here rather than off integration.updated.
|
||||
case "mcp.status.changed":
|
||||
if (bootstrapping) break
|
||||
void result.location.mcp.refresh(event.location)
|
||||
break
|
||||
}
|
||||
|
|
@ -750,7 +752,8 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
|
|||
}
|
||||
|
||||
async function bootstrap() {
|
||||
const settled = await Promise.allSettled([
|
||||
if (bootstrapping) return bootstrapping
|
||||
bootstrapping = Promise.allSettled([
|
||||
sdk.api.session
|
||||
.list({
|
||||
limit: 50,
|
||||
|
|
@ -787,14 +790,23 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
|
|||
result.location.skill.refresh(),
|
||||
result.shell.refresh(),
|
||||
])
|
||||
for (const failure of settled.filter((item) => item.status === "rejected"))
|
||||
console.error("Failed to refresh default location data", failure.reason)
|
||||
.then((settled) => {
|
||||
for (const failure of settled.filter((item) => item.status === "rejected"))
|
||||
console.error("Failed to refresh default location data", failure.reason)
|
||||
})
|
||||
.finally(() => {
|
||||
bootstrapping = undefined
|
||||
})
|
||||
return bootstrapping
|
||||
}
|
||||
|
||||
onCleanup(
|
||||
sdk.event.listen(({ details }) => {
|
||||
if (details.type === "server.connected") {
|
||||
void bootstrap()
|
||||
return
|
||||
}
|
||||
handleEvent(details)
|
||||
if (details.type === "server.connected") void bootstrap()
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { onCleanup, onMount } from "solid-js"
|
|||
import { createStore } from "solid-js/store"
|
||||
import { createSimpleContext } from "./helper"
|
||||
|
||||
export type SDKConnectionStatus = "connecting" | "connected" | "reconnecting"
|
||||
export type SDKConnectionStatus = "connected" | "connecting"
|
||||
|
||||
type SDKEventMap = { [Type in V2Event["type"]]: Extract<V2Event, { type: Type }> }
|
||||
const connectTimeout = 2_000
|
||||
|
|
@ -64,10 +64,11 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
|||
return connection.signal.reason instanceof Error
|
||||
? connection.signal.reason
|
||||
: new Error("Event stream disconnected")
|
||||
if (first.value.type !== "server.connected") return new Error("Event stream did not start with server.connected")
|
||||
clearTimeout(timeout)
|
||||
attempt = 0
|
||||
setConnection({ status: "connected", attempt: 0, error: undefined })
|
||||
events.emit(first.value.type, first.value)
|
||||
setConnection({ status: "connected", attempt: 0, error: undefined })
|
||||
connected()
|
||||
while (!abort.signal.aborted && !controller.signal.aborted) {
|
||||
const event = await iterator.next()
|
||||
|
|
@ -84,7 +85,7 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
|||
if (abort.signal.aborted || controller.signal.aborted) return
|
||||
attempt += 1
|
||||
setConnection({
|
||||
status: "reconnecting",
|
||||
status: "connecting",
|
||||
attempt,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ test("reconnects the event stream and bootstraps fresh data", async () => {
|
|||
expect(data.connection.attempt()).toBe(0)
|
||||
|
||||
events.disconnect()
|
||||
await wait(() => data.connection.status() === "reconnecting")
|
||||
await wait(() => data.connection.status() === "connecting")
|
||||
expect(data.connection.attempt()).toBe(1)
|
||||
expect(data.connection.error()).toBe("Event stream disconnected")
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue