Open History to see the last commit
1import type {
2 ChatGptHandoffState,
3 ProviderAuthState,
4 ProviderInstallState,
5} from "@t3tools/contracts";
6
7export function readCodexSetupMode(config: unknown): "managed" | "existing" {
8 return config !== null &&
9 typeof config === "object" &&
10 "setupMode" in config &&
11 config.setupMode === "managed"
12 ? "managed"
13 : "existing";
14}
15
16const RUNNING_INSTALL_PHASES = new Set(["downloading", "extracting", "verifying"]);
17
18/**
19 * Whether the `started` install left a usable runtime; `undefined` until settled.
20 * The subscription can still carry the previous operation's state when start
21 * returns, so only a state with the same operation id settles a running install.
22 */
23export function settleProviderInstall(
24 started: ProviderInstallState,
25 current: ProviderInstallState | null,
26): boolean | undefined {
27 const state = RUNNING_INSTALL_PHASES.has(started.phase)
28 ? current?.operationId === started.operationId
29 ? current
30 : null
31 : started;
32 if (state === null || RUNNING_INSTALL_PHASES.has(state.phase)) return undefined;
33 return state.phase !== "failed" && state.phase !== "cancelled" && state.installedVersion !== null;
34}
35
36/**
37 * The authorization page for a sign-in flow: a URL once the flow waits on the
38 * browser, `null` once it ends without one, `undefined` while it is still
39 * starting. A `flowId` limits this to one flow, because auth state is shared
40 * per instance and another client can start a flow of its own.
41 */
42export function settleAuthorizationPage(
43 state: ProviderAuthState | null,
44 flowId: string | null,
45): string | null | undefined {
46 if (state === null || state.flowId === null) return undefined;
47 if (flowId !== null && state.flowId !== flowId) return undefined;
48 if (state.phase === "succeeded" || state.phase === "failed" || state.phase === "cancelled")
49 return null;
50 if (state.phase !== "waiting") return undefined;
51 const url =
52 state.interaction?.type === "browser" ? state.interaction.url : state.authorizationUrl;
53 return url ?? undefined;
54}
55
56export function settleHandoffAuthorizationPage(
57 state: ChatGptHandoffState | null,
58): string | null | undefined {
59 if (state === null) return undefined;
60 return state.phase === "finished" ? null : settleAuthorizationPage(state.state, null);
61}