1import { resolveManagedCodexHomeLayout } from "./CodexManagedHome.ts";
2import { CodexSettings, ProviderSetupError, type ProviderInstanceId } from "@t3tools/contracts";
3import * as Effect from "effect/Effect";
4import * as FileSystem from "effect/FileSystem";
5import * as Path from "effect/Path";
6import * as Schema from "effect/Schema";
7import * as ServerConfig from "../config.ts";
8import * as CodexInstallation from "./CodexInstallation.ts";
9import { makeCodexChatGptAuth } from "./CodexChatGptAuth.ts";
10import { materializeCodexShadowHome } from "./Drivers/CodexHomeLayout.ts";
11
12export interface CodexEffectiveRuntime {
13 readonly config: CodexSettings;
14 readonly environment: NodeJS.ProcessEnv;
15 readonly revision: string;
16}
17const decodeSettings = Schema.decodeSync(CodexSettings);
18// Managed sign-in stores tokens in T3's credential store and never writes native auth.json.
19const managedCodexLaunchArgs = [
20 'model_provider="openai_token_sharing"',
21 'model_providers.openai_token_sharing.name="OpenAI Token Sharing"',
22 'model_providers.openai_token_sharing.base_url="https://api.openai.com/v1"',
23 'model_providers.openai_token_sharing.model_catalog_url="https://api.openai.com/v1/models"',
24 "features.api_key_model_discovery=true",
25 "features.multi_agent=false",
26 "features.multi_agent_v2=false",
27 'model_providers.openai_token_sharing.env_key="ACCESS_TOKEN"',
28 'model_providers.openai_token_sharing.wire_api="responses"',
29 "model_providers.openai_token_sharing.requires_openai_auth=false",
30 "model_providers.openai_token_sharing.supports_websockets=false",
31]
32 .map((value) => `-c '${value}'`)
33 .join(" ");
34
35export const makeCodexManagedRuntime = Effect.fn("makeCodexManagedRuntime")(function* (options: {
36 readonly instanceId: ProviderInstanceId;
37 readonly enabled: boolean;
38 readonly environment: NodeJS.ProcessEnv;
39 readonly config: CodexSettings;
40}) {
41 const installation = yield* CodexInstallation.CodexInstallation;
42 const config = yield* ServerConfig.ServerConfig;
43 const fs = yield* FileSystem.FileSystem;
44 const path = yield* Path.Path;
45 const auth = yield* makeCodexChatGptAuth({
46 instanceId: options.instanceId,
47 defaultReturnUrl: new URL(
48 "/welcome",
49 config.devUrl ?? `http://localhost:${config.port}`,
50 ).toString(),
51 });
52 const homeLayout = yield* resolveManagedCodexHomeLayout(
53 config.stateDir,
54 options.instanceId,
55 options.config,
56 );
57 const homePath = homeLayout.effectiveHomePath ?? homeLayout.sharedHomePath;
58 const resolve = Effect.gen(function* () {
59 const executable = yield* installation.acquire().pipe(
60 Effect.mapError(
61 () =>
62 new ProviderSetupError({
63 instanceId: options.instanceId,
64 operation: "install",
65 detail: "Set up managed Codex before starting a session.",
66 }),
67 ),
68 );
69 const credentials = yield* auth.access;
70 yield* materializeCodexShadowHome(homeLayout).pipe(
71 Effect.provideService(FileSystem.FileSystem, fs),
72 Effect.provideService(Path.Path, path),
73 Effect.mapError(
74 (cause) =>
75 new ProviderSetupError({
76 instanceId: options.instanceId,
77 operation: "runtime",
78 detail: cause.message,
79 }),
80 ),
81 );
82 yield* fs.makeDirectory(homePath, { recursive: true }).pipe(
83 Effect.mapError(
84 () =>
85 new ProviderSetupError({
86 instanceId: options.instanceId,
87 operation: "runtime",
88 detail: "Could not prepare the managed Codex runtime.",
89 }),
90 ),
91 );
92 // Ambient CLI overrides cannot redirect a T3-owned token to a different provider.
93 const environment: NodeJS.ProcessEnv = {
94 ...options.environment,
95 ACCESS_TOKEN: credentials.accessToken,
96 CODEX_HOME: homePath,
97 };
98 delete environment.T3CODE_CODEX_LAUNCH_ARGS;
99 delete environment.OPENAI_API_KEY;
100 delete environment.OPENAI_BASE_URL;
101 return {
102 config: decodeSettings({
103 enabled: options.enabled,
104 setupMode: "managed",
105 binaryPath: executable.executablePath,
106 homePath,
107 launchArgs: managedCodexLaunchArgs,
108 }),
109 environment,
110 revision: credentials.accessToken,
111 } satisfies CodexEffectiveRuntime;
112 });
113 return { auth, resolve, installation, homePath, homeLayout };
114});