1import {
2 AntigravitySettings,
3 ClaudeSettings,
4 CodexSettings,
5 type ServerSettings,
6 UsageLimitSourceConfig,
7 secretSettingsFields,
8} from "@t3tools/contracts";
9import { acpRegistryClient } from "@t3tools/provider-acp-registry/client";
10import { cursorClient } from "@t3tools/provider-cursor/client";
11import { grokClient } from "@t3tools/provider-grok/client";
12import { museClient } from "@t3tools/provider-muse/client";
13import { openCodeClient } from "@t3tools/provider-opencode/client";
14import { piClient } from "@t3tools/provider-pi/client";
15import {
16 readSecretFieldValues,
17 SOURCE_CONTROL_HOST_SECRET_FIELDS,
18 sourceControlHostSecretName,
19 writeSecretFieldValues,
20} from "./sourceControl/sourceControlHostSecrets.ts";
21
22export const SECRET_REDACTED = "\u2022\u2022\u2022\u2022\u2022\u2022";
23
24const providerSecretFields = new Map([
25 ["codex", secretSettingsFields(CodexSettings)],
26 ["claudeAgent", secretSettingsFields(ClaudeSettings)],
27 ["antigravity", secretSettingsFields(AntigravitySettings)],
28 ...[cursorClient, grokClient, museClient, openCodeClient, piClient, acpRegistryClient].map(
29 (definition) =>
30 [definition.driverKind, secretSettingsFields(definition.settingsSchema)] as const,
31 ),
32]);
33const usageLimitSecretFields = secretSettingsFields(UsageLimitSourceConfig);
34const encode = (value: string) => Buffer.from(value, "utf8").toString("base64url");
35
36export interface SettingsSecret {
37 readonly secretName: string;
38 readonly kind: "field" | "environment";
39 readonly value: string;
40 readonly redacted: boolean;
41 readonly sensitive: boolean;
42 readonly providerInstanceId?: string;
43 readonly environmentVariable?: string;
44}
45
46type SecretValue = Pick<SettingsSecret, "value" | "redacted">;
47
48/**
49 * Visits credential fields declared by schemas and dynamically sensitive environment variables.
50 * Store keys and client representations stay here; callers supply only the secret operation.
51 */
52export function mapSettingsSecrets(
53 settings: ServerSettings,
54 map: (secret: SettingsSecret) => SecretValue,
55): ServerSettings {
56 const mapFields = (
57 fields: Readonly<Record<string, unknown>>,
58 secretFields: ReadonlyArray<string>,
59 name: (field: string, serverHost: string | null) => string,
60 ) => {
61 const next = { ...fields };
62 for (const field of secretFields) {
63 if (!(field in next)) continue;
64 next[field] = writeSecretFieldValues(
65 next[field],
66 readSecretFieldValues(next[field]).map(([serverHost, value]) => {
67 const result = map({
68 secretName: name(field, serverHost),
69 kind: "field",
70 value,
71 redacted: value === SECRET_REDACTED,
72 sensitive: true,
73 });
74 return [serverHost, result.redacted ? SECRET_REDACTED : result.value] as const;
75 }),
76 );
77 }
78 return next;
79 };
80
81 const providerInstances = Object.fromEntries(
82 Object.entries(settings.providerInstances).map(([instanceId, instance]) => {
83 const config = instance.config;
84 return [
85 instanceId,
86 {
87 ...instance,
88 ...(config !== null && typeof config === "object" && !Array.isArray(config)
89 ? {
90 config: mapFields(
91 config as Record<string, unknown>,
92 providerSecretFields.get(instance.driver) ?? [],
93 (field, serverHost) =>
94 `provider-config-${encode(instanceId)}-${encode(instance.driver)}-${encode(field)}` +
95 (serverHost === null ? "" : `-${encode(serverHost)}`),
96 ),
97 }
98 : {}),
99 ...(instance.environment
100 ? {
101 environment: instance.environment.map((variable) => {
102 const result = map({
103 secretName: `provider-env-${encode(instanceId)}-${encode(variable.name)}`,
104 kind: "environment",
105 value: variable.value,
106 redacted: variable.valueRedacted === true,
107 sensitive: variable.sensitive,
108 providerInstanceId: instanceId,
109 environmentVariable: variable.name,
110 });
111 const { valueRedacted: _omit, ...rest } = variable;
112 return {
113 ...rest,
114 value: result.value,
115 ...(result.redacted ? { valueRedacted: true } : {}),
116 };
117 }),
118 }
119 : {}),
120 },
121 ];
122 }),
123 );
124
125 const sourceControlHosts = Object.fromEntries(
126 Object.entries(settings.sourceControlHosts).map(([kind, fields]) => [
127 kind,
128 mapFields(fields, SOURCE_CONTROL_HOST_SECRET_FIELDS.get(kind) ?? [], (field, serverHost) =>
129 sourceControlHostSecretName({ kind, field, serverHost }),
130 ),
131 ]),
132 ) as ServerSettings["sourceControlHosts"];
133
134 const usageLimitSources = Object.fromEntries(
135 Object.entries(settings.usageLimitSources).map(([id, source]) => [
136 id,
137 mapFields(
138 source,
139 usageLimitSecretFields,
140 (field, serverHost) =>
141 `usage-limit-source-${encode(id)}` +
142 (field === "managementKey" && serverHost === null
143 ? ""
144 : `-${encode(field)}${serverHost === null ? "" : `-${encode(serverHost)}`}`),
145 ),
146 ]),
147 ) as ServerSettings["usageLimitSources"];
148
149 return { ...settings, providerInstances, sourceControlHosts, usageLimitSources };
150}