1import {
2 ClaudeSettings,
3 CodexSettings,
4 type ExecutionEnvironmentPlatformOs,
5 type ServerProvider,
6 type ServerSettings,
7} from "@t3tools/contracts";
8import * as Option from "effect/Option";
9import * as Schema from "effect/Schema";
10
11const decodeClaudeSettings = Schema.decodeUnknownOption(ClaudeSettings);
12const decodeCodexSettings = Schema.decodeUnknownOption(CodexSettings);
13const SAFE_SHELL_BINARY_PATTERN = /^[A-Za-z0-9_./:\\-]+$/;
14
15function quoteProviderBinary(
16 binaryPath: string,
17 fallback: string,
18 platform: ExecutionEnvironmentPlatformOs,
19): string {
20 if (
21 SAFE_SHELL_BINARY_PATTERN.test(binaryPath) &&
22 (platform === "windows" || !binaryPath.includes("\\"))
23 ) {
24 return binaryPath;
25 }
26 if (platform === "windows") return `& '${binaryPath.replaceAll("'", "''")}'`;
27 if (platform === "darwin" || platform === "linux") {
28 if (binaryPath.startsWith("~/") || binaryPath.startsWith("~\\")) {
29 return `~/'${binaryPath.slice(2).replaceAll("'", `'"'"'`)}'`;
30 }
31 return `'${binaryPath.replaceAll("'", `'"'"'`)}'`;
32 }
33 return fallback;
34}
35
36export function getOnboardingProviderState(provider: ServerProvider | undefined) {
37 if (provider === undefined) return "checking";
38 if (!provider.enabled || provider.status === "disabled") return "disabled";
39 if (!provider.installed && provider.status === "warning" && provider.auth.status === "unknown")
40 return "checking";
41 if (!provider.installed) return "install";
42 if (provider.auth.status === "unauthenticated") return "signIn";
43 if (provider.status === "ready") return "ready";
44 return "attention";
45}
46
47// These need no Node/npm and install where provider maintenance recognizes native binaries.
48const NATIVE_INSTALL_COMMANDS = {
49 claudeAgent: {
50 windows: "irm https://claude.ai/install.ps1 | iex",
51 posix: "curl -fsSL https://claude.ai/install.sh | bash",
52 },
53 codex: {
54 windows: "irm https://chatgpt.com/codex/install.ps1 | iex",
55 posix: "curl -fsSL https://chatgpt.com/codex/install.sh | sh",
56 },
57} as const;
58
59export function resolveOnboardingProviderInstallCommand(
60 driver: keyof typeof NATIVE_INSTALL_COMMANDS,
61 platform: ExecutionEnvironmentPlatformOs,
62): string {
63 const commands = NATIVE_INSTALL_COMMANDS[driver];
64 return platform === "windows" ? commands.windows : commands.posix;
65}
66
67export function resolveOnboardingProviderLoginCommand(
68 provider: ServerProvider,
69 settings: ServerSettings,
70 platform: ExecutionEnvironmentPlatformOs,
71): string {
72 const instance = settings.providerInstances[provider.instanceId];
73
74 if (provider.driver === "claudeAgent") {
75 const config = decodeClaudeSettings(instance?.config ?? {});
76 const binaryPath = Option.isSome(config) ? config.value.binaryPath : "claude";
77 return `${quoteProviderBinary(binaryPath, "claude", platform)} auth login`;
78 }
79
80 if (provider.driver === "codex") {
81 const config = decodeCodexSettings(instance?.config ?? {});
82 const binaryPath = Option.isSome(config) ? config.value.binaryPath : "codex";
83 return `${quoteProviderBinary(binaryPath, "codex", platform)} login`;
84 }
85
86 return provider.driver;
87}