1// @effect-diagnostics nodeBuiltinImport:off - node:http sends the one-shot loopback callback with no proxy, redirect handling, or response logging.
2import * as NodeHttp from "node:http";
3
4import { ProviderSetupError, type ProviderInstanceId } from "@t3tools/contracts";
5import * as Effect from "effect/Effect";
6
7export interface AntigravityPendingCallback {
8 readonly redirectUri: string;
9 readonly state: string;
10}
11
12/** Only the callback advertised by this running ACP process may receive a request. */
13export const validateAntigravityCallbackUrl = Effect.fn("validateAntigravityCallbackUrl")(
14 function* (
15 instanceId: ProviderInstanceId,
16 pending: AntigravityPendingCallback,
17 callbackUrl: string,
18 ) {
19 const invalid = (detail: string) =>
20 new ProviderSetupError({ instanceId, operation: "complete", detail });
21 if (callbackUrl.length > 16_384) {
22 return yield* invalid("The sign-in response URL is too long.");
23 }
24 const callback = yield* Effect.try({
25 try: () => new URL(callbackUrl),
26 catch: () => invalid("Paste the complete redirect URL from the Google sign-in page."),
27 });
28 const expected = new URL(pending.redirectUri);
29 if (
30 callback.protocol !== "http:" ||
31 callback.hostname !== "127.0.0.1" ||
32 callback.origin !== expected.origin ||
33 callback.pathname !== expected.pathname ||
34 callback.username !== "" ||
35 callback.password !== "" ||
36 callback.hash !== ""
37 ) {
38 return yield* invalid("This redirect URL does not belong to the current sign-in.");
39 }
40 const states = callback.searchParams.getAll("state");
41 if (states.length !== 1 || states[0] !== pending.state) {
42 return yield* invalid("This redirect URL does not belong to the current sign-in.");
43 }
44 const codes = callback.searchParams.getAll("code");
45 const errors = callback.searchParams.getAll("error");
46 if (
47 !(
48 (codes.length === 1 && Boolean(codes[0]) && errors.length === 0) ||
49 (errors.length === 1 && Boolean(errors[0]) && codes.length === 0)
50 )
51 ) {
52 return yield* invalid("The redirect URL must contain one Google sign-in response.");
53 }
54 const issuers = callback.searchParams.getAll("iss");
55 if (
56 issuers.length > 1 ||
57 (issuers.length === 1 && issuers[0] !== "https://accounts.google.com")
58 ) {
59 return yield* invalid("The redirect URL is not a Google sign-in response.");
60 }
61 return callback;
62 },
63);
64
65/** Sends one callback, without proxies, redirects, readiness probes, or response logging. */
66export const forwardAntigravityCallback = (
67 instanceId: ProviderInstanceId,
68 callback: URL,
69): Effect.Effect<void, ProviderSetupError> =>
70 Effect.callback<void, ProviderSetupError>((resume) => {
71 const failed = () =>
72 new ProviderSetupError({
73 instanceId,
74 operation: "complete",
75 detail: "Could not deliver the sign-in response. Start sign-in again.",
76 });
77 let response: NodeHttp.IncomingMessage | undefined;
78 const request = NodeHttp.request(
79 {
80 protocol: "http:",
81 hostname: callback.hostname,
82 port: callback.port,
83 path: `${callback.pathname}${callback.search}`,
84 method: "GET",
85 agent: false,
86 },
87 (incoming) => {
88 response = incoming;
89 incoming.once("error", () => resume(Effect.fail(failed())));
90 incoming.once("end", () => {
91 const status = incoming.statusCode ?? 0;
92 resume(status >= 200 && status < 300 ? Effect.void : Effect.fail(failed()));
93 });
94 incoming.resume();
95 },
96 );
97 request.once("error", () => resume(Effect.fail(failed())));
98 request.end();
99 return Effect.sync(() => {
100 request.destroy();
101 response?.destroy();
102 });
103 }).pipe(
104 Effect.timeoutOrElse({
105 duration: "10 seconds",
106 orElse: () =>
107 Effect.fail(
108 new ProviderSetupError({
109 instanceId,
110 operation: "complete",
111 detail: "The sign-in response timed out. Start sign-in again.",
112 }),
113 ),
114 }),
115 );