Skip to content

Commit ec44e4e

Browse files
committed
fix(usage): stop Devin's quota request from following redirects
1 parent 9b19a86 commit ec44e4e

2 files changed

Lines changed: 34 additions & 20 deletions

File tree

‎packages/provider-acp-registry/src/server/devinUsageLimits.test.ts‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import * as NodeCrypto from "@effect/platform-node/NodeCrypto";
22
import * as NodeServices from "@effect/platform-node/NodeServices";
33
import { describe, expect, it } from "@effect/vitest";
4+
import * as Context from "effect/Context";
45
import * as Effect from "effect/Effect";
56
import * as FileSystem from "effect/FileSystem";
67
import * as Layer from "effect/Layer";
78
import * as Schema from "effect/Schema";
9+
import * as FetchHttpClient from "effect/http/FetchHttpClient";
810
import * as HttpClient from "effect/http/HttpClient";
911
import type * as HttpClientRequest from "effect/http/HttpClientRequest";
1012
import * as HttpClientResponse from "effect/http/HttpClientResponse";
@@ -32,12 +34,17 @@ const quotaResponse = (planStatus: Record<string, unknown>) =>
3234
interface CapturedRequest {
3335
readonly url: string;
3436
readonly apiKey: string | undefined;
37+
readonly redirect: RequestInit["redirect"];
3538
}
3639

3740
const recordingClient = (captured: CapturedRequest[], response: Response) =>
38-
HttpClient.make((request) =>
41+
HttpClient.make((request, _url, _signal, fiber) =>
3942
Effect.sync(() => {
40-
captured.push({ url: request.url, apiKey: requestApiKey(request) });
43+
captured.push({
44+
url: request.url,
45+
apiKey: requestApiKey(request),
46+
redirect: Context.getOrUndefined(fiber.context, FetchHttpClient.RequestInit)?.redirect,
47+
});
4148
return HttpClientResponse.fromWeb(request, response);
4249
}),
4350
);
@@ -158,6 +165,7 @@ describe("readDevinUsageLimits", () => {
158165
"https://server.codeium.com/exa.seat_management_pb.SeatManagementService/GetUserStatus",
159166
);
160167
expect(captured[0]?.apiKey).toBe("stored-key");
168+
expect(captured[0]?.redirect).toBe("error");
161169
}).pipe(Effect.provide(Layer.mergeAll(NodeServices.layer, NodeCrypto.layer))),
162170
);
163171

‎packages/provider-acp-registry/src/server/devinUsageLimits.ts‎

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as Option from "effect/Option";
77
import * as Path from "effect/Path";
88
import * as Schema from "effect/Schema";
99
import * as Hex from "effect/encoding/Hex";
10+
import * as FetchHttpClient from "effect/http/FetchHttpClient";
1011
import * as HttpClient from "effect/http/HttpClient";
1112
import * as HttpClientRequest from "effect/http/HttpClientRequest";
1213
import * as HttpClientResponse from "effect/http/HttpClientResponse";
@@ -21,6 +22,9 @@ import * as HostProcess from "@t3tools/shared/HostProcess";
2122
const DEFAULT_API_SERVER_URL = "https://server.codeium.com";
2223
const USER_STATUS_PATH = "/exa.seat_management_pb.SeatManagementService/GetUserStatus";
2324

25+
/** The API key in the request body must never follow a redirect off the API server. */
26+
const NO_REDIRECT: RequestInit = { redirect: "error" };
27+
2428
const tomlString = (contents: string, key: string): string | undefined =>
2529
new RegExp(`^\\s*${key}\\s*=\\s*"([^"\\n]*)"`, "m").exec(contents)?.[1];
2630

@@ -99,24 +103,26 @@ export const readDevinUsageLimits = Effect.fn("readDevinUsageLimits")(function*
99103
if (!apiUrl || apiUrl.protocol !== "https:") return unsupported;
100104

101105
const client = yield* HttpClient.HttpClient;
102-
const response = yield* client.execute(
103-
HttpClientRequest.post(`${apiUrl.origin}${USER_STATUS_PATH}`).pipe(
104-
HttpClientRequest.setHeaders({
105-
"content-type": "application/json",
106-
"connect-protocol-version": "1",
107-
}),
108-
HttpClientRequest.bodyJsonUnsafe({
109-
metadata: {
110-
apiKey,
111-
ideName: "t3code",
112-
ideVersion: "0.0.0",
113-
extensionName: "t3code",
114-
extensionVersion: "0.0.0",
115-
locale: "en",
116-
},
117-
}),
118-
),
119-
);
106+
const response = yield* client
107+
.execute(
108+
HttpClientRequest.post(`${apiUrl.origin}${USER_STATUS_PATH}`).pipe(
109+
HttpClientRequest.setHeaders({
110+
"content-type": "application/json",
111+
"connect-protocol-version": "1",
112+
}),
113+
HttpClientRequest.bodyJsonUnsafe({
114+
metadata: {
115+
apiKey,
116+
ideName: "t3code",
117+
ideVersion: "0.0.0",
118+
extensionName: "t3code",
119+
extensionVersion: "0.0.0",
120+
locale: "en",
121+
},
122+
}),
123+
),
124+
)
125+
.pipe(Effect.provideService(FetchHttpClient.RequestInit, NO_REDIRECT));
120126
if (response.status === 401 || response.status === 403) return unsupported;
121127
const body = yield* HttpClientResponse.filterStatusOk(response).pipe(
122128
Effect.flatMap(HttpClientResponse.schemaBodyJson(GetUserStatusResponse)),

0 commit comments

Comments
 (0)