| 1 | import { |
| 2 | AntigravitySettings, |
| 3 | ClaudeSettings, |
| 4 | CodexSettings, |
| 5 | type ServerSettings, |
| 6 | UsageLimitSourceConfig, |
| 7 | secretSettingsFields, |
| 8 | } from "@t3tools/contracts"; |
| 9 | import { acpRegistryClient } from "@t3tools/provider-acp-registry/client"; |
| 10 | import { cursorClient } from "@t3tools/provider-cursor/client"; |
| 11 | import { grokClient } from "@t3tools/provider-grok/client"; |
| 12 | import { museClient } from "@t3tools/provider-muse/client"; |
| 13 | import { openCodeClient } from "@t3tools/provider-opencode/client"; |
| 14 | import { piClient } from "@t3tools/provider-pi/client"; |
| 15 | import { |
| 16 | readSecretFieldValues, |
| 17 | SOURCE_CONTROL_HOST_SECRET_FIELDS, |
| 18 | sourceControlHostSecretName, |
| 19 | writeSecretFieldValues, |
| 20 | } from "./sourceControl/sourceControlHostSecrets.ts"; |
| 21 | |
| 22 | export const SECRET_REDACTED = "\u2022\u2022\u2022\u2022\u2022\u2022"; |
| 23 | |
| 24 | const 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 | ]); |
| 33 | const usageLimitSecretFields = secretSettingsFields(UsageLimitSourceConfig); |
| 34 | const encode = (value: string) => Buffer.from(value, "utf8").toString("base64url"); |
| 35 | |
| 36 | export 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 | |
| 46 | type 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 | */ |
| 52 | export 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 | } |