fix(web): avoid live 404 probes
This commit is contained in:
@@ -46,6 +46,7 @@ export function isCloudApiProxyRoute(method, pathname) {
|
||||
}
|
||||
if (normalizedMethod === "POST") {
|
||||
return POST_PROXY_ROUTES.has(normalizedPath) ||
|
||||
normalizedPath.startsWith("/v1/rpc/") ||
|
||||
normalizedPath.startsWith("/v1/agent/sessions/") ||
|
||||
isDevicePodPostProxyRoute(normalizedPath) ||
|
||||
isWorkbenchWorkspaceWriteProxyRoute(normalizedMethod, normalizedPath);
|
||||
|
||||
@@ -24,6 +24,21 @@ test("cloud web proxies skills routes through authenticated cloud-api", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("cloud web proxies authenticated REST RPC bridge routes", () => {
|
||||
assert.deepEqual(cloudWebProxyRoutePolicy("POST", "/v1/rpc/system.health"), {
|
||||
proxy: true,
|
||||
authRequired: true,
|
||||
publicRoute: false,
|
||||
routeKey: "POST /v1/rpc/system.health"
|
||||
});
|
||||
assert.deepEqual(cloudWebProxyRoutePolicy("GET", "/v1/rpc/system.health"), {
|
||||
proxy: true,
|
||||
authRequired: true,
|
||||
publicRoute: false,
|
||||
routeKey: "GET /v1/rpc/system.health"
|
||||
});
|
||||
});
|
||||
|
||||
test("cloud web proxies only supported authenticated device-pod write routes", () => {
|
||||
assert.deepEqual(cloudWebProxyRoutePolicy("POST", "/v1/device-pods/D601-F103-V2/jobs"), {
|
||||
proxy: true,
|
||||
|
||||
@@ -57,6 +57,10 @@ assertIncludes(appSource, "SkillsView", "skills component must exist");
|
||||
assertIncludes(appSource, "SettingsView", "settings component must exist");
|
||||
assertIncludes(appTsx, "useWorkbenchStore(auth.authState === \"authenticated\")", "Workbench data requests must wait until auth is authenticated");
|
||||
assertIncludes(apiClientSource, "adapter: (): Promise<ApiResult<LiveProbePayload>> => fetchJson(\"/v1/rpc/system.health\", { method: \"POST\"", "adapter health must use POST /v1/rpc/system.health");
|
||||
assertIncludes(appSource, "selectVisibleDevicePodId", "Device Pod detail probes must be selected from the authenticated visible list");
|
||||
assertIncludes(appSource, "api.devicePods()", "Device Pod list must be fetched before detail probes");
|
||||
assert.doesNotMatch(appSource, /api\.devicePods\(\),\s*api\.devicePodStatus/u, "Device Pod status must not be requested in the initial parallel live probe");
|
||||
assertIncludes(readRepo("internal/dev-entrypoint/cloud-web-routes.mjs"), "normalizedPath.startsWith(\"/v1/rpc/\")", "Cloud Web must proxy REST RPC bridge routes to cloud-api");
|
||||
assertIncludes(appSource, "id=\"command-input\"", "React runtime must render command input selector");
|
||||
assertIncludes(appSource, "id=\"conversation-list\"", "React runtime must render conversation list selector");
|
||||
assertIncludes(appSource, "id=\"device-pod-sidebar\"", "React runtime must render Device Pod sidebar selector");
|
||||
|
||||
@@ -3,10 +3,14 @@ import { useCallback, useEffect, useMemo, useReducer } from "react";
|
||||
import { api } from "../services/api/client";
|
||||
import type {
|
||||
AgentChatResponse,
|
||||
ApiResult,
|
||||
ChatMessage,
|
||||
CodeAgentAvailability,
|
||||
DevicePodEventsResponse,
|
||||
ConversationRecord,
|
||||
DevicePodItem,
|
||||
DevicePodListResponse,
|
||||
DevicePodStatusResponse,
|
||||
LiveSurface,
|
||||
ProviderProfile,
|
||||
SessionTab,
|
||||
@@ -129,16 +133,19 @@ export function useWorkbenchStore(enabled: boolean): WorkbenchStore {
|
||||
}, []);
|
||||
|
||||
const refreshLive = useCallback(async (devicePodId?: string) => {
|
||||
const selected = devicePodId ?? state.selectedDevicePodId;
|
||||
const [healthLive, health, restIndex, adapter, devicePods, devicePodStatus, devicePodEvents] = await Promise.all([
|
||||
const requestedDevicePodId = devicePodId ?? state.selectedDevicePodId;
|
||||
const [healthLive, health, restIndex, adapter, devicePods] = await Promise.all([
|
||||
api.healthLive(),
|
||||
api.health(),
|
||||
api.restIndex(),
|
||||
api.adapter(),
|
||||
api.devicePods(),
|
||||
api.devicePodStatus(selected),
|
||||
api.devicePodEvents(selected)
|
||||
api.devicePods()
|
||||
]);
|
||||
const visibleDevicePods = devicePodsFromResult(devicePods);
|
||||
const selected = selectVisibleDevicePodId(devicePods.data, visibleDevicePods, requestedDevicePodId);
|
||||
const [devicePodStatus, devicePodEvents] = selected
|
||||
? await Promise.all([api.devicePodStatus(selected), api.devicePodEvents(selected)])
|
||||
: [null, null] satisfies [ApiResult<DevicePodStatusResponse> | null, ApiResult<DevicePodEventsResponse> | null];
|
||||
const live: LiveSurface = { healthLive, health, restIndex, adapter, devicePods, devicePodStatus, devicePodEvents, loadedAt: new Date().toISOString() };
|
||||
dispatch({ type: "live:set", live, availability: availabilityFromLive(live), selectedDevicePodId: selected });
|
||||
}, [state.selectedDevicePodId]);
|
||||
@@ -320,3 +327,15 @@ export function devicePodsFromLive(live: LiveSurface | null): DevicePodItem[] {
|
||||
const payload = live?.devicePods.data;
|
||||
return payload?.devicePods ?? payload?.items ?? [];
|
||||
}
|
||||
|
||||
function devicePodsFromResult(result: ApiResult<DevicePodListResponse>): DevicePodItem[] {
|
||||
return result.data?.devicePods ?? result.data?.items ?? [];
|
||||
}
|
||||
|
||||
function selectVisibleDevicePodId(payload: DevicePodListResponse | null, pods: DevicePodItem[], requestedDevicePodId: string): string {
|
||||
const payloadSelected = nonEmptyString(payload?.selectedDevicePodId);
|
||||
const requested = nonEmptyString(requestedDevicePodId);
|
||||
const requestedVisible = requested ? pods.some((pod) => pod.devicePodId === requested) : false;
|
||||
if (requested && requestedVisible) return requested;
|
||||
return payloadSelected ?? pods[0]?.devicePodId ?? "";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user