feat(workbench): integrate runtime data and map tooling

This commit is contained in:
2026-09-22 10:45:46 +08:00
parent bf44cc0f25
commit bebcf2a06c
163 changed files with 15642 additions and 1312 deletions
+14
View File
@@ -122,6 +122,20 @@ describe("Agent API client sessions", () => {
expect(headers.get("Content-Type")).toBe("application/json");
});
it("sends the authorized project context on Agent requests", async () => {
const fetchMock = vi.fn(async (_input: RequestInfo | URL, _init?: RequestInit) =>
new Response(JSON.stringify({ sessions: [] }), { status: 200 })
);
vi.stubGlobal("fetch", fetchMock);
const getProjectId = vi.fn().mockResolvedValue("project-117");
await createAgentApiClient("http://agent.local", { getProjectId }).listSessions();
expect(getProjectId).toHaveBeenCalledOnce();
const headers = new Headers(fetchMock.mock.calls[0]?.[1]?.headers);
expect(headers.get("X-Project-Id")).toBe("project-117");
});
it("streams session events from the backend SSE endpoint", async () => {
vi.stubGlobal(
"fetch",
+11 -14
View File
@@ -115,6 +115,7 @@ export type AgentApiClient = {
export type AgentApiClientOptions = {
getAccessToken?: AccessTokenProvider;
getProjectId?: () => Promise<string>;
};
const AGENT_API_BASE_URLS = [env.TJWATER_AGENT_API_BASE_URL.replace(/\/$/, "")];
@@ -137,7 +138,8 @@ export function createAgentApiClient(
setActiveBaseUrl,
path,
init,
options.getAccessToken
options.getAccessToken,
options.getProjectId
);
const requestJson = async <T,>(path: string, init?: RequestInit) => {
const response = await request(path, init);
@@ -298,18 +300,19 @@ async function fetchWithFallback(
setActiveBaseUrl: (baseUrl: string) => void,
path: string,
init?: RequestInit,
getAccessToken?: AccessTokenProvider
getAccessToken?: AccessTokenProvider,
getProjectId?: () => Promise<string>
) {
const orderedBaseUrls = [activeBaseUrl, ...baseUrls.filter((item) => item !== activeBaseUrl)];
let lastError: unknown;
let lastResponse: Response | null = null;
const accessToken = await getAccessToken?.();
const requestInit = accessToken
? {
...init,
headers: withBearerToken(init?.headers, accessToken)
}
: init;
const projectId = await getProjectId?.();
const hasContextHeaders = Boolean(accessToken || projectId);
const headers = hasContextHeaders ? new Headers(init?.headers) : undefined;
if (accessToken) headers?.set("Authorization", `Bearer ${accessToken}`);
if (projectId) headers?.set("X-Project-Id", projectId);
const requestInit = hasContextHeaders ? { ...init, headers } : init;
for (const baseUrl of orderedBaseUrls) {
try {
@@ -337,12 +340,6 @@ async function fetchWithFallback(
throw lastError instanceof Error ? lastError : new Error("Agent API unavailable");
}
function withBearerToken(headersInit: HeadersInit | undefined, accessToken: string) {
const headers = new Headers(headersInit);
headers.set("Authorization", `Bearer ${accessToken}`);
return headers;
}
function shouldFallbackOnHttpStatus(status: number) {
return status === 404 || status === 405 || status === 502 || status === 503 || status === 504;
}