picoclaw/web/frontend/src/hooks/use-gateway.ts
wenjie 8a44410e37
feat: add web gateway hot reload and polling state sync (#1684)
* feat(gateway): support hot reload and empty startup

- extract gateway runtime into pkg/gateway
- add gateway.hot_reload config with default and example values
- allow starting the gateway without a default model via --allow-empty
- stop treating missing enabled channels as a startup error
- update related tests

* feat: replace gateway SSE updates with polling-based state sync

- remove gateway SSE broadcasting and event endpoint
- add polling-based gateway status refresh with stopping state handling
- detect when gateway restart is required after default model changes
- resolve gateway health and websocket proxy targets from configured host
- update gateway UI labels and add backend/frontend test coverage
2026-03-17 18:46:00 +08:00

74 lines
1.8 KiB
TypeScript

import { useAtomValue } from "jotai"
import { useCallback, useEffect, useState } from "react"
import { restartGateway, startGateway, stopGateway } from "@/api/gateway"
import {
beginGatewayStoppingTransition,
cancelGatewayStoppingTransition,
gatewayAtom,
refreshGatewayState,
subscribeGatewayPolling,
updateGatewayStore,
} from "@/store"
export function useGateway() {
const gateway = useAtomValue(gatewayAtom)
const { status: state, canStart, restartRequired } = gateway
const [loading, setLoading] = useState(false)
useEffect(() => {
return subscribeGatewayPolling()
}, [])
const start = useCallback(async () => {
if (!canStart) return
setLoading(true)
try {
await startGateway()
updateGatewayStore({
status: "starting",
restartRequired: false,
})
} catch (err) {
console.error("Failed to start gateway:", err)
} finally {
await refreshGatewayState({ force: true })
setLoading(false)
}
}, [canStart])
const stop = useCallback(async () => {
setLoading(true)
beginGatewayStoppingTransition()
try {
await stopGateway()
} catch (err) {
console.error("Failed to stop gateway:", err)
cancelGatewayStoppingTransition()
} finally {
await refreshGatewayState({ force: true })
setLoading(false)
}
}, [])
const restart = useCallback(async () => {
if (state !== "running") return
setLoading(true)
try {
await restartGateway()
updateGatewayStore({
status: "restarting",
restartRequired: false,
})
} catch (err) {
console.error("Failed to restart gateway:", err)
} finally {
await refreshGatewayState({ force: true })
setLoading(false)
}
}, [state])
return { state, loading, canStart, restartRequired, start, stop, restart }
}