revert(cli): keep HeyGen API traffic on stable prod (#3202)

* Revert "fix(cli): route HeyGen API calls through canary (#3201)"

This reverts commit 5545521556.

* fix(cli): remove remaining EF canary routes
This commit is contained in:
Miguel Ángel
2026-08-10 21:41:21 -04:00
committed by GitHub
parent 5545521556
commit 08934bfd55
10 changed files with 23 additions and 76 deletions
-2
View File
@@ -84,7 +84,6 @@ describe("auth/client", () => {
authorization: "Bearer at_123", authorization: "Bearer at_123",
[HEYGEN_CLI_SOURCE_HEADER]: HEYGEN_CLI_SOURCE, [HEYGEN_CLI_SOURCE_HEADER]: HEYGEN_CLI_SOURCE,
[HEYGEN_CLIENT_SOURCE_HEADER]: HEYGEN_CLIENT_SOURCE, [HEYGEN_CLIENT_SOURCE_HEADER]: HEYGEN_CLIENT_SOURCE,
heygen_route: "canary",
}); });
}); });
@@ -92,7 +91,6 @@ describe("auth/client", () => {
expect(buildAuthHeaders(apiKeyCred())).toEqual({ expect(buildAuthHeaders(apiKeyCred())).toEqual({
"x-api-key": "hg_x", "x-api-key": "hg_x",
[HEYGEN_CLIENT_SOURCE_HEADER]: HEYGEN_CLIENT_SOURCE, [HEYGEN_CLIENT_SOURCE_HEADER]: HEYGEN_CLIENT_SOURCE,
heygen_route: "canary",
}); });
}); });
+3 -7
View File
@@ -19,7 +19,6 @@ import { ErrApi, ErrUnauthenticated, isAuthError } from "./errors.js";
import type { ResolvedCredential } from "./resolver.js"; import type { ResolvedCredential } from "./resolver.js";
import { scrubCredentials } from "./scrub.js"; import { scrubCredentials } from "./scrub.js";
import type { OAuthTokens } from "./store.js"; import type { OAuthTokens } from "./store.js";
import { withHeygenCanaryRoute } from "../utils/heygenRoute.js";
const DEFAULT_BASE_URL = "https://api.heygen.com"; const DEFAULT_BASE_URL = "https://api.heygen.com";
export const HEYGEN_CLI_SOURCE_HEADER = "X-HeyGen-Source"; export const HEYGEN_CLI_SOURCE_HEADER = "X-HeyGen-Source";
@@ -186,20 +185,17 @@ export class AuthClient {
export function buildAuthHeaders(credential: ResolvedCredential): Record<string, string> { export function buildAuthHeaders(credential: ResolvedCredential): Record<string, string> {
if (credential.type === "oauth") { if (credential.type === "oauth") {
return withHeygenCanaryRoute({ return {
authorization: `Bearer ${credential.access_token}`, authorization: `Bearer ${credential.access_token}`,
[HEYGEN_CLI_SOURCE_HEADER]: HEYGEN_CLI_SOURCE, [HEYGEN_CLI_SOURCE_HEADER]: HEYGEN_CLI_SOURCE,
[HEYGEN_CLIENT_SOURCE_HEADER]: HEYGEN_CLIENT_SOURCE, [HEYGEN_CLIENT_SOURCE_HEADER]: HEYGEN_CLIENT_SOURCE,
}); };
} }
// API-key traffic keeps the normal billing path; the backend ignores the // API-key traffic keeps the normal billing path; the backend ignores the
// cli-source header for it, so we don't send it (avoids a contradictory // cli-source header for it, so we don't send it (avoids a contradictory
// "cli-source claim on an API-key request"). The tool-attribution header IS // "cli-source claim on an API-key request"). The tool-attribution header IS
// sent here — an API-key hyperframes call is still hyperframes usage. // sent here — an API-key hyperframes call is still hyperframes usage.
return withHeygenCanaryRoute({ return { "x-api-key": credential.key, [HEYGEN_CLIENT_SOURCE_HEADER]: HEYGEN_CLIENT_SOURCE };
"x-api-key": credential.key,
[HEYGEN_CLIENT_SOURCE_HEADER]: HEYGEN_CLIENT_SOURCE,
});
} }
async function safeText(res: Response): Promise<string> { async function safeText(res: Response): Promise<string> {
+1 -33
View File
@@ -173,10 +173,8 @@ describe("auth/oauth", () => {
it("posts grant_type=refresh_token and persists the response", async () => { it("posts grant_type=refresh_token and persists the response", async () => {
process.env["HEYGEN_API_URL"] = "https://api.test.example"; process.env["HEYGEN_API_URL"] = "https://api.test.example";
let capturedBody: string | undefined; let capturedBody: string | undefined;
let capturedHeaders: HeadersInit | undefined;
const fetchImpl = (async (_url: string, init?: RequestInit) => { const fetchImpl = (async (_url: string, init?: RequestInit) => {
capturedBody = init?.body as string; capturedBody = init?.body as string;
capturedHeaders = init?.headers;
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
access_token: "new_at", access_token: "new_at",
@@ -194,7 +192,6 @@ describe("auth/oauth", () => {
expect(tokens.refresh_token).toBe("new_rt"); expect(tokens.refresh_token).toBe("new_rt");
expect(capturedBody).toContain("grant_type=refresh_token"); expect(capturedBody).toContain("grant_type=refresh_token");
expect(capturedBody).toContain("refresh_token=old_rt"); expect(capturedBody).toContain("refresh_token=old_rt");
expect(capturedHeaders).toMatchObject({ heygen_route: "canary" });
// Should have persisted. // Should have persisted.
const { credentials } = await readStore(); const { credentials } = await readStore();
@@ -298,10 +295,8 @@ describe("auth/oauth", () => {
it("sends token_type_hint when provided", async () => { it("sends token_type_hint when provided", async () => {
let capturedBody = ""; let capturedBody = "";
let capturedHeaders: HeadersInit | undefined;
const fetchImpl = (async (_url: string, init?: RequestInit) => { const fetchImpl = (async (_url: string, init?: RequestInit) => {
capturedBody = init?.body as string; capturedBody = init?.body as string;
capturedHeaders = init?.headers;
return new Response("", { status: 200 }); return new Response("", { status: 200 });
}) as unknown as typeof fetch; }) as unknown as typeof fetch;
await revokeTokens("tok", { await revokeTokens("tok", {
@@ -309,7 +304,6 @@ describe("auth/oauth", () => {
token_type_hint: "refresh_token", token_type_hint: "refresh_token",
}); });
expect(capturedBody).toContain("token_type_hint=refresh_token"); expect(capturedBody).toContain("token_type_hint=refresh_token");
expect(capturedHeaders).toMatchObject({ heygen_route: "canary" });
}); });
it("returns silently when client_id is unconfigured (no throw)", async () => { it("returns silently when client_id is unconfigured (no throw)", async () => {
@@ -343,21 +337,6 @@ describe("auth/oauth", () => {
}); });
describe("startAuthorizationCodeFlow persistence", () => { describe("startAuthorizationCodeFlow persistence", () => {
it("routes the authorization-code exchange through canary", async () => {
let capturedHeaders: HeadersInit | undefined;
const fetchImpl = (async (_url: string | URL | Request, init?: RequestInit) => {
capturedHeaders = init?.headers;
return new Response(JSON.stringify({ access_token: "new_at" }), {
status: 200,
headers: { "content-type": "application/json" },
});
}) as typeof fetch;
await startAuthorizationCodeFlow({ fetchImpl });
expect(capturedHeaders).toMatchObject({ heygen_route: "canary" });
});
it("overwrites the OAuth block on fresh login (no inherited refresh_token)", async () => { it("overwrites the OAuth block on fresh login (no inherited refresh_token)", async () => {
// Pre-seed a prior session whose refresh_token must NOT leak into // Pre-seed a prior session whose refresh_token must NOT leak into
// the new login when the new response omits one. // the new login when the new response omits one.
@@ -471,11 +450,7 @@ describe("auth/oauth", () => {
}); });
it("polls pending and slow_down responses without persisting before identity verification", async () => { it("polls pending and slow_down responses without persisting before identity verification", async () => {
const requests: Array<{ const requests: Array<{ url: string; body: URLSearchParams }> = [];
url: string;
body: URLSearchParams;
headers: HeadersInit | undefined;
}> = [];
const responses = [ const responses = [
new Response( new Response(
JSON.stringify({ JSON.stringify({
@@ -510,7 +485,6 @@ describe("auth/oauth", () => {
requests.push({ requests.push({
url: String(url), url: String(url),
body: new URLSearchParams(String(init?.body ?? "")), body: new URLSearchParams(String(init?.body ?? "")),
headers: init?.headers,
}); });
}); });
const sleeps: number[] = []; const sleeps: number[] = [];
@@ -540,12 +514,6 @@ describe("auth/oauth", () => {
expect(requests[1]?.body.get("grant_type")).toBe( expect(requests[1]?.body.get("grant_type")).toBe(
"urn:ietf:params:oauth:grant-type:device_code", "urn:ietf:params:oauth:grant-type:device_code",
); );
expect(requests.map(({ headers }) => headers)).toEqual([
expect.objectContaining({ heygen_route: "canary" }),
expect.objectContaining({ heygen_route: "canary" }),
expect.objectContaining({ heygen_route: "canary" }),
expect.objectContaining({ heygen_route: "canary" }),
]);
expect((await readStore()).source).toBe("absent"); expect((await readStore()).source).toBe("absent");
await persistFreshOAuth(tokens); await persistFreshOAuth(tokens);
+9 -12
View File
@@ -54,7 +54,6 @@ import {
type StoredUserInfo, type StoredUserInfo,
} from "./store.js"; } from "./store.js";
import { c } from "../ui/colors.js"; import { c } from "../ui/colors.js";
import { withHeygenCanaryRoute } from "../utils/heygenRoute.js";
const REVOKE_TIMEOUT_MS = 5_000; const REVOKE_TIMEOUT_MS = 5_000;
const MIN_EXPIRES_IN_SECONDS = 30; const MIN_EXPIRES_IN_SECONDS = 30;
@@ -256,10 +255,10 @@ async function requestDeviceAuthorization(
async (signal) => { async (signal) => {
const response = await runtime.fetchImpl(deviceAuthorizationEndpoint(), { const response = await runtime.fetchImpl(deviceAuthorizationEndpoint(), {
method: "POST", method: "POST",
headers: withHeygenCanaryRoute({ headers: {
"content-type": "application/x-www-form-urlencoded", "content-type": "application/x-www-form-urlencoded",
accept: "application/json", accept: "application/json",
}), },
body: new URLSearchParams({ client_id: runtime.clientId, scope }).toString(), body: new URLSearchParams({ client_id: runtime.clientId, scope }).toString(),
signal, signal,
}); });
@@ -304,10 +303,10 @@ async function requestDeviceToken(
async (signal) => { async (signal) => {
const response = await runtime.fetchImpl(tokenEndpoint(), { const response = await runtime.fetchImpl(tokenEndpoint(), {
method: "POST", method: "POST",
headers: withHeygenCanaryRoute({ headers: {
"content-type": "application/x-www-form-urlencoded", "content-type": "application/x-www-form-urlencoded",
accept: "application/json", accept: "application/json",
}), },
body: new URLSearchParams({ body: new URLSearchParams({
grant_type: DEVICE_CODE_GRANT_TYPE, grant_type: DEVICE_CODE_GRANT_TYPE,
device_code: deviceCode, device_code: deviceCode,
@@ -395,10 +394,10 @@ export async function refreshTokens(
const res = await fetchImpl(tokenEndpoint(), { const res = await fetchImpl(tokenEndpoint(), {
method: "POST", method: "POST",
headers: withHeygenCanaryRoute({ headers: {
"content-type": "application/x-www-form-urlencoded", "content-type": "application/x-www-form-urlencoded",
accept: "application/json", accept: "application/json",
}), },
body: body.toString(), body: body.toString(),
}); });
@@ -447,9 +446,7 @@ export async function revokeTokens(token: string, opts: RevokeOptions = {}): Pro
try { try {
const res = await fetchImpl(revokeEndpoint(), { const res = await fetchImpl(revokeEndpoint(), {
method: "POST", method: "POST",
headers: withHeygenCanaryRoute({ headers: { "content-type": "application/x-www-form-urlencoded" },
"content-type": "application/x-www-form-urlencoded",
}),
body: body.toString(), body: body.toString(),
signal: controller.signal, signal: controller.signal,
}); });
@@ -510,10 +507,10 @@ async function exchangeCodeForTokens(args: {
}); });
const res = await fetchImpl(tokenEndpoint(), { const res = await fetchImpl(tokenEndpoint(), {
method: "POST", method: "POST",
headers: withHeygenCanaryRoute({ headers: {
"content-type": "application/x-www-form-urlencoded", "content-type": "application/x-www-form-urlencoded",
accept: "application/json", accept: "application/json",
}), },
body: body.toString(), body: body.toString(),
}); });
if (res.status === 400 || res.status === 401) { if (res.status === 400 || res.status === 401) {
-9
View File
@@ -1,9 +0,0 @@
const HEYGEN_ROUTE_HEADER = "heygen_route";
const HEYGEN_CANARY_ROUTE = "canary";
/** Route CLI-owned HeyGen API calls through the EF canary deployment. */
export function withHeygenCanaryRoute(
headers: Record<string, string> = {},
): Record<string, string> {
return { ...headers, [HEYGEN_ROUTE_HEADER]: HEYGEN_CANARY_ROUTE };
}
@@ -36,7 +36,6 @@ describeE2E("publish stable-URL round trip (live server)", () => {
async function fetchPublicProject(projectId: string): Promise<Record<string, unknown>> { async function fetchPublicProject(projectId: string): Promise<Record<string, unknown>> {
const response = await fetch( const response = await fetch(
`${getPublishApiBaseUrl()}/v1/hyperframes/projects/${projectId}/public`, `${getPublishApiBaseUrl()}/v1/hyperframes/projects/${projectId}/public`,
{ headers: { heygen_route: "canary" } },
); );
expect(response.ok).toBe(true); expect(response.ok).toBe(true);
const payload = (await response.json()) as { data: Record<string, unknown> }; const payload = (await response.json()) as { data: Record<string, unknown> };
@@ -551,7 +551,7 @@ afterEach(() => {
vi.unstubAllEnvs(); vi.unstubAllEnvs();
}); });
const jsonHeaders = { "content-type": "application/json", heygen_route: "canary" }; const jsonHeaders = { "content-type": "application/json" };
const signedStagedS3Url = const signedStagedS3Url =
"https://s3.example.com/upload?X-Amz-SignedHeaders=content-length;content-type;host;x-amz-server-side-encryption"; "https://s3.example.com/upload?X-Amz-SignedHeaders=content-length;content-type;host;x-amz-server-side-encryption";
@@ -662,7 +662,7 @@ describe("publishProjectArchive", () => {
expect(fetchMock).toHaveBeenCalledTimes(2); expect(fetchMock).toHaveBeenCalledTimes(2);
expectFetchCall(fetchMock, 2, "https://api2.heygen.com/v1/hyperframes/projects/publish", { expectFetchCall(fetchMock, 2, "https://api2.heygen.com/v1/hyperframes/projects/publish", {
method: "POST", method: "POST",
headers: { heygen_route: "canary" }, headers: {},
}); });
} finally { } finally {
rmSync(dir, { recursive: true, force: true }); rmSync(dir, { recursive: true, force: true });
+5 -6
View File
@@ -5,7 +5,6 @@ import AdmZip from "adm-zip";
import ignore, { type Ignore } from "ignore"; import ignore, { type Ignore } from "ignore";
import { CSS_URL_RE, isNonRelativeUrl, isPathInside } from "@hyperframes/core"; import { CSS_URL_RE, isNonRelativeUrl, isPathInside } from "@hyperframes/core";
import { buildAuthHeaders } from "../auth/client.js"; import { buildAuthHeaders } from "../auth/client.js";
import { withHeygenCanaryRoute } from "./heygenRoute.js";
import { tryResolveCredential } from "../auth/index.js"; import { tryResolveCredential } from "../auth/index.js";
import { writeProjectLink } from "./projectLink.js"; import { writeProjectLink } from "./projectLink.js";
@@ -558,7 +557,7 @@ async function publishProjectArchiveDirect(
"file", "file",
new File([archiveArrayBuffer(archive)], `${title}.zip`, { type: PUBLISH_CONTENT_TYPE }), new File([archiveArrayBuffer(archive)], `${title}.zip`, { type: PUBLISH_CONTENT_TYPE }),
); );
const headers = withHeygenCanaryRoute(authHeaders); const headers: Record<string, string> = { ...authHeaders };
const response = await fetchForPublish( const response = await fetchForPublish(
`${apiBaseUrl}/v1/hyperframes/projects/publish`, `${apiBaseUrl}/v1/hyperframes/projects/publish`,
@@ -621,10 +620,10 @@ async function publishProjectArchiveStaged(
content_type: PUBLISH_CONTENT_TYPE, content_type: PUBLISH_CONTENT_TYPE,
content_length: archive.buffer.byteLength, content_length: archive.buffer.byteLength,
}), }),
headers: withHeygenCanaryRoute({ headers: {
...authHeaders, ...authHeaders,
"content-type": "application/json", "content-type": "application/json",
}), },
signal: AbortSignal.timeout(PUBLISH_METADATA_TIMEOUT_MS), signal: AbortSignal.timeout(PUBLISH_METADATA_TIMEOUT_MS),
}), }),
"Failed to prepare project upload", "Failed to prepare project upload",
@@ -654,10 +653,10 @@ async function publishProjectArchiveStaged(
...(isPublic ? { is_public: true } : {}), ...(isPublic ? { is_public: true } : {}),
...(projectId ? { project_id: projectId } : {}), ...(projectId ? { project_id: projectId } : {}),
}), }),
headers: withHeygenCanaryRoute({ headers: {
...authHeaders, ...authHeaders,
"content-type": "application/json", "content-type": "application/json",
}), },
signal: AbortSignal.timeout(uploadTimeoutMs(archive.buffer.byteLength)), signal: AbortSignal.timeout(uploadTimeoutMs(archive.buffer.byteLength)),
}), }),
"Failed to finalize project publish", "Failed to finalize project publish",
@@ -35,7 +35,7 @@ describe("submitFeedback", () => {
"https://api.example.com/v1/hyperframes/feedback", "https://api.example.com/v1/hyperframes/feedback",
expect.objectContaining({ expect.objectContaining({
method: "POST", method: "POST",
headers: { "content-type": "application/json", heygen_route: "canary" }, headers: { "content-type": "application/json" },
body: JSON.stringify({ body: JSON.stringify({
rating: 4, rating: 4,
rating_scale: 10, rating_scale: 10,
+2 -3
View File
@@ -1,6 +1,5 @@
import { getPublishApiBaseUrl } from "./publishProject.js"; import { getPublishApiBaseUrl } from "./publishProject.js";
import { FEEDBACK_RATING_SCALE } from "./feedbackRating.js"; import { FEEDBACK_RATING_SCALE } from "./feedbackRating.js";
import { withHeygenCanaryRoute } from "./heygenRoute.js";
// Match the backend DTO caps (HyperframesFeedbackRequest). Truncate here so an // Match the backend DTO caps (HyperframesFeedbackRequest). Truncate here so an
// over-long field (e.g. a pasted stack trace) is still forwarded truncated, // over-long field (e.g. a pasted stack trace) is still forwarded truncated,
@@ -31,9 +30,9 @@ export async function submitFeedback(input: {
cli_version: cap(input.cliVersion, MAX_CLI_VERSION), cli_version: cap(input.cliVersion, MAX_CLI_VERSION),
env: cap(input.env, MAX_ENV), env: cap(input.env, MAX_ENV),
}), }),
headers: withHeygenCanaryRoute({ headers: {
"content-type": "application/json", "content-type": "application/json",
}), },
signal: AbortSignal.timeout(5000), signal: AbortSignal.timeout(5000),
}); });
} catch { } catch {