148a48a215
The Cursor run RPC is HTTP/2-only (api2 rejects HTTP/1.1 with 464), and bun only opens an HTTP/2 session when TLS-ALPN negotiates h2. Behind an ALPN-stripping TLS-intercepting proxy (e.g. Zscaler) the handshake yields no h2 and bun throws ERR_HTTP2_ERROR: "h2 is not supported", which streamCursor passed through verbatim. Model discovery masks the same failure by falling back to bundled models.json, so listing works while runs fail opaquely. Map the h2-negotiation failure on both the session and request error paths to a ProviderResponseError that names the ALPN-stripping proxy as the cause and points at the providers.cursor.baseUrl HTTP/2 bridge workaround. Fixes #5828
38 lines
1.6 KiB
TypeScript
38 lines
1.6 KiB
TypeScript
import { describe, expect, it } from "bun:test";
|
|
import { ProviderResponseError } from "@oh-my-pi/pi-ai/error";
|
|
import { mapH2TransportError } from "@oh-my-pi/pi-ai/providers/cursor";
|
|
|
|
const BASE_URL = "https://api2.cursor.sh";
|
|
|
|
describe("mapH2TransportError", () => {
|
|
it("rewrites the opaque bun ALPN failure into an actionable Cursor error", () => {
|
|
const raw = Object.assign(new Error("h2 is not supported"), { code: "ERR_HTTP2_ERROR" });
|
|
const mapped = mapH2TransportError(raw, BASE_URL);
|
|
expect(mapped).toBeInstanceOf(ProviderResponseError);
|
|
const err = mapped as ProviderResponseError;
|
|
expect(err.provider).toBe("cursor");
|
|
expect(err.kind).toBe("runtime");
|
|
expect(err.message).toContain(BASE_URL);
|
|
expect(err.message).toContain("ALPN");
|
|
expect(err.message).toContain("providers.cursor.baseUrl");
|
|
expect(err.cause).toBe(raw);
|
|
});
|
|
|
|
it("matches the h2-not-supported message case-insensitively", () => {
|
|
const raw = Object.assign(new Error("H2 Is Not Supported"), { code: "ERR_HTTP2_ERROR" });
|
|
expect(mapH2TransportError(raw, BASE_URL)).toBeInstanceOf(ProviderResponseError);
|
|
});
|
|
|
|
it("passes through an HTTP/2 error whose message is unrelated to ALPN", () => {
|
|
const raw = Object.assign(new Error("Stream closed with error code NGHTTP2_INTERNAL_ERROR"), {
|
|
code: "ERR_HTTP2_ERROR",
|
|
});
|
|
expect(mapH2TransportError(raw, BASE_URL)).toBe(raw);
|
|
});
|
|
|
|
it("passes through a non-HTTP/2 error even when it mentions h2", () => {
|
|
const raw = Object.assign(new Error("h2 is not supported"), { code: "ECONNRESET" });
|
|
expect(mapH2TransportError(raw, BASE_URL)).toBe(raw);
|
|
});
|
|
});
|